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

来源:互联网 发布:网络飞机票怎么取登机 编辑:程序博客网 时间:2024/06/06 11:35

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> T createService(Class<T> clazz) {        return retrofit.create(clazz);    }}

这就是定义的接口类:

/** * 接口定义 */public interface ApiService {    @FormUrlEncoded    @POST("/sys/sendMsg")    Observable<BaseEntity<DataEntity>> getData(@FieldMap Map<String, String> params);}

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

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

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

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

public class BaseEntity<T> 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<T> call(Observable<T> tObservable) {        return tObservable                .subscribeOn(Schedulers.io())                .unsubscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .map(new Func1<T, T>() {// 通用错误处理,判断code                    @Override                    public T call(T t) {                        if (((BaseEntity<T>)t).getStatus_code() != 10000) {                            throw new ApiException(((BaseEntity<T>)t).getStatus_code(), ((BaseEntity<T>)t).getError_msg());                        }                        return t;                    }                });    }    public static <T> DefaultTransformer<T> 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<BaseEntity<DataEntity>> getData(@FieldMap Map<String, String> 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<String, String> params = new HashMap<>();        params.put("user_name", "");        params.put("user_pwd", "");        Subscription subscribe = HttpMethods.getInstance()                .createService(ApiService.class)                .getData(params)                .compose(DefaultTransformer.<BaseEntity<DataEntity>>create())                .subscribe(new ApiSubscriber<BaseEntity<DataEntity>>(this) {                    @Override                    public void onNext(BaseEntity<DataEntity> entity) {                    }                });        addSubscription(subscribe);//添加订阅    }}

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

源码地址

阅读全文
2 0