RxJava + Retrofit2.0的项目实战完美封装

Retrofit 和RxJava已经出来很久了,从去年开始rxjava和retrofit就开始火,所以之前在做项目的时候也用了rxjava和retrofit,今天就介绍一下在项目中如何封装rxjava和retrofit。对于 RxJava 不是很了解的同学推荐你们看这篇文章给 Android 开发者的 RxJava 详解。Retrofit的使用可以看看Android Retrofit 2.0使用。

首先在我们的工程的build.gradle中添加依赖:

    compile 'io.reactivex:rxjava:1.1.8'
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'

然后封装retrofit:

public class HttpMethods {
    //接口根地址
    public static final String BASE_URL = "http://www.baidu.com";
    //设置超时时间
    private static final long DEFAULT_TIMEOUT = 10_000L;

    private Retrofit retrofit;
    private OkHttpClient client;

    private static class SingletonHolder {
        private static final HttpMethods INSTANCE = new HttpMethods();
    }
    //私有化构造方法
    private HttpMethods() {
        client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
                //添加请求头
                //.addInterceptor(new HeaderInterceptor())
                //添加日志打印拦截器
                .addInterceptor(new LoggerInterceptor("===", true))
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                //添加Gson解析
                .addConverterFactory(GsonConverterFactory.create())
                //添加rxjava
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
    }

    public static HttpMethods getInstance() {
        return SingletonHolder.INSTANCE;
    }
    //这里返回一个泛型类,主要返回的是定义的接口类
    public  T createService(Class clazz) {
        return retrofit.create(clazz);
    }

}

这就是定义的接口类:

/**
 * 接口定义
 */
public interface ApiService {

    @FormUrlEncoded
    @POST("/sys/sendMsg")
    Observable> getData(@FieldMap Map params);

}

这里的根地址和接口中的地址都是随便写的一个地址,用的时候替换成自己项目的地址就行了。添加请求头的话,由于项目中没用到所以直接注释了。
接下来就是封装服务器请求和返回数据。一般情况下返回的数据结构是这样的:

{
   "status_code":10000,
   "error_msg":"请求成功!",
   "data":{
         "name":"张三",
         "age":3
   }
}

如果你们的服务器返回不是这样的格式那你就只有坐下来请他喝茶,跟他好好说了。大不了就怼他。

对于这样的数据我们肯定要对status_code做出一些判断,不同的status_code对应不同的错误信息。所以我们新建一个BaseEntity,对应上面的数据结构。

public class BaseEntity implements Serializable {

    private int status_code;
    private String error_msg;
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public int getStatus_code() {
        return status_code;
    }

    public void setStatus_code(int status_code) {
        this.status_code = status_code;
    }

    public String getError_msg() {
        return error_msg;
    }

    public void setError_msg(String error_msg) {
        this.error_msg = error_msg;
    }
}

这就是所有实体的一个基类,data可以为任何数据类型,所以我们使用泛型。
我们要对所以返回结果进行预处理,新建一个DefaultTransformer继承Observable.Transformer,预处理无非就是对status_code进行判断和解析,不同的错误返回不同的错误信息。有个操作符compose。因为我们在每一个请求中都会处理status_code以及使用一些操作符,比如用observeOn和subscribeOn来切换线程。RxJava提供了一种解决方案:Transformer(转换器),一般情况下就是通过使用操作符compose()来实现。

代码:

public class DefaultTransformer<T> implements Observable.Transformer<T, T> {
    @Override
    public Observable call(Observable tObservable) {
        return tObservable
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1() {// 通用错误处理,判断code
                    @Override
                    public T call(T t) {
                        if (((BaseEntity)t).getStatus_code() != 10000) {
                            throw new ApiException(((BaseEntity)t).getStatus_code(), ((BaseEntity)t).getError_msg());
                        }
                        return t;
                    }
                });
    }

    public static  DefaultTransformer create() {
        return new DefaultTransformer<>();
    }
}

这里我们使用map操作符把Obserable< BaseEntity< T > >,转换成为Observable< T >在内部对status_code进行了预处理。这里当状态码不等于10000就表示请求出错抛出异常。这里的ApiException是我们自定义的一个异常类,用来处理服务器返回的异常。

代码:

public class ApiException extends IllegalArgumentException {

    private int code;

    public ApiException(int code, String msg) {
        super(msg);
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

接下来就是处理网络请求的操作,和显示加载等待的dialog:

public abstract class ApiSubscriber<T> extends Subscriber<T> {

    private LoadingDialog mDialog;

    public ApiSubscriber() {

    }

    public ApiSubscriber(@NonNull Context context) {
        mDialog = new LoadingDialog(context);
    }

    @Override
    public void onStart() {
        if (mDialog != null)
            mDialog.show();
    }

    @Override
    public void onCompleted() {
        if (mDialog != null && mDialog.isShowing())
            mDialog.dismiss();
    }

    /**
     * 只要链式调用中抛出了异常都会走这个回调
     */
    @Override
    public void onError(Throwable e) {
        if (mDialog != null && mDialog.isShowing())
            mDialog.dismiss();
        if (e instanceof ApiException) {
            //处理服务器返回的错误
        } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
            ToastUtils.showShort("网络异常,请检查网络");
        } else if (e instanceof TimeoutException || e instanceof SocketTimeoutException) {
            ToastUtils.showShort("网络不畅,请稍后再试!");
        } else if (e instanceof JsonSyntaxException) {
            ToastUtils.showShort("数据解析异常");
        } else {
            ToastUtils.showShort("服务端错误");
        }
        e.printStackTrace();
    }
}

这里新建一个ApiSubscriber类继承Subscriber类,写了两个构造方法,一个是显示dialog,一个是不显示dialog。重写了三个方法,这里我们没有重写onNext方法,因为这个方法是请求成功返回数据的,所以我们等到请求数据界面去重写。在onError里面做了所有的错误处理,在里面可以根据服务器返回的错误码对不同的错误做不同的处理。

接下来的话就是管理生命周期了。有个专门的库可以管理生命周期的叫RxLifecycle,可以去看看。不过我们不用这个,我们在BaseActivity里面写。

public abstract class BaseActivity extends AppCompatActivity {

    private CompositeSubscription mCompositeSubscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        onUnsubscribe();
    }
    /**
     * 所有rx订阅后,需要调用此方法,用于在detachView时取消订阅
     */
    protected void addSubscription(Subscription subscribe) {
        if (mCompositeSubscription == null)
            mCompositeSubscription = new CompositeSubscription();
        mCompositeSubscription.add(subscribe);
    }

    /**
     * 取消本页面所有订阅
     */
    protected void onUnsubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }

}

我们在请求数据的地方调用addSubscription(Subscription subscribe)方法,当activity在onDestroy()的时候就取消订阅了。

最后就是我们的请求数据了,这是之前定义的一个伪接口:

public interface ApiService {

    @FormUrlEncoded
    @POST("/sys/sendMsg")
    Observable> getData(@FieldMap Map params);

}

然后我们在activity里面这样写:

public class MainActivity extends BaseActivity {

    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv_content);
        findViewById(R.id.btn_request).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getData();
            }
        });
    }

    private void getData() {
        Map params = new HashMap<>();
        params.put("user_name", "");
        params.put("user_pwd", "");
        Subscription subscribe = HttpMethods.getInstance()
                .createService(ApiService.class)
                .getData(params)
                .compose(DefaultTransformer.>create())
                .subscribe(new ApiSubscriber>(this) {
                    @Override
                    public void onNext(BaseEntity entity) {

                    }
                });
        addSubscription(subscribe);//添加订阅
    }
}

在onNext方法里面就能获取到我们的数据了,当然这个接口是请求不成功的,在使用的时候替换成自己的接口就行了。retrofit还有很多的注解,这里只是使用了@FormUrlEncoded注解(表单的形式)做个示例。到这里封装就基本完成了,我也是菜鸟,希望大家多提意见,互相学习。

源码地址

你可能感兴趣的:(Rxjava)