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注解(表单的形式)做个示例。到这里封装就基本完成了,我也是菜鸟,希望大家多提意见,互相学习。
源码地址