Retrofit2 RxJava lambda

近期看到很多关于RxJava的项目,一开始还不太明白,简单的说就是用观察者模式实现的异步操作,用于代替过去的AsyncTask和Handle。

早先在使用网络访问中一直使用的是 okhttp,后来发现 Retrofit2 像注解式,这两个同样属于square,然后就想用Retrofit2结合RxJava弄一个网络访问的请求,在RxJava中会用到java8的lambda,写法看起来挺溜的 ( ) -> { } 。 最近用的时候踩了不少坑,后面会有填坑的方法。

Retrofit2 ( okhttp + jackson + rxjava )

RxJava ( rxandroid extends rxjava)

初次使用

引入关联、lambda支持
/* build.gradle */
classpath 'me.tatarka:gradle-retrolambda:3.2.5'

/* app.gradle*/
apply plugin: 'me.tatarka.retrolambda'

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
 }

//引入
dependencies {
    compile 'com.squareup.retrofit2:retrofit:2.0.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
    compile 'com.squareup.retrofit2:converter-jackson:2.0.0'
    compile 'io.reactivex:rxjava:1.1.2'
    compile 'io.reactivex:rxandroid:1.1.0'
}
Retrofit基本配置
/**
 * Retrofit基本配置
 * Created by 剑指锁妖塔 on 2016/3/22.
 */
public class AHttp {

    private static final String baseUrl = "http://fylder.me/photo/";

    /**
     * 获取一个ApiService
     *
     * @return ApiService
     */
    public static <T> T getInstance(final Class<T> service) {
        Retrofit retrofit = new Retrofit.Builder()             
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())   //RxJava回调
                .addConverterFactory(new StringConverterFactory())          //解析String
                .addConverterFactory(JacksonConverterFactory.create())      //jackson解析json
                .baseUrl(baseUrl)
                .build();
        return retrofit.create(service);
    }
}
定义请求接口
/**
 * 定义数据请求api
 * Created by 剑指锁妖塔 on 2016/3/22.
 */
public interface UserServerImpl {

    @FormUrlEncoded
    @POST("user/login_account")
    Observable<LoginEntity> login(@Field("user") String user, @Field("pass") String pass);
}
网络请求
/**
 * 网络请求
 * Created by 剑指锁妖塔 on 2016/3/22.
 */
public class UserHttpRequest {

    /**
    * 返回一个Observable
    */
    public static Observable<LoginEntity> login(String username, String password) {

        UserServerImpl server = AHttp.getInstance(UserServerImpl.class);
        Observable<LoginEntity> login = server.login(username, password);

        return login.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());
    }
}
执行
void post() {
     UserHttpRequest.login("fylder", "123").subscribe(
             s -> Logger.json(s.toString()),
             throwable -> Logger.e(throwable.getMessage()));
}

subscribe(final Action1<? super T> onNext, final Action1 onError) 会回调两个参数,第一个正常返回值,第二个为错误的响应。


以下在开发时遇到的问题

Q1: @POST传参问题 get请求在使用@Query()注入参数是没问题,但在post请求就莫名其妙提示服务器接收不到,得换@Field配合@FormUrlEncoded,原因是服务器需要表格提交,当然一般情况下不要求form-data的形式@Query()也是可行。

get方式

@GET("user/login_account")
Observable<LoginEntity> login(@Query("user") String user, @Query("pass") String pass);

post方式

@FormUrlEncoded
@POST("user/login_account")
Observable<LoginEntity> login(@Field("user") String user, @Field("pass") String pass);


Q2: Observable<String> 当需要返回String类型时,仅使用默认的json解析会出问题,这就要在Retrofit配置时加入一个过滤转换器,而且要放在json解析转换器之前,否则无效。

.addConverterFactory(new StringConverterFactory()) //解析String

/**
 * String转换器
 * Created by 剑指锁妖塔 on 2016/3/22.
 */
public class StringConverterFactory extends Converter.Factory {

    private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");

    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    
        if (String.class.equals(type)) {
            return new Converter<ResponseBody, String>() {
                @Override
                public String convert(ResponseBody value) throws IOException {
                    return value.string();
                }
            };
        }
        return null;
    }

    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {

        if (String.class.equals(type)) {
            return new Converter<String, RequestBody>() {
                @Override
                public RequestBody convert(String value) throws IOException {
                    return RequestBody.create(MEDIA_TYPE, value);
                }
            };
        }
        return null;
    }
}