一、说明
RxJava和Retrofit的基本用法这里不再阐述,网络上有很多教程,这里只写进一步封装的过程,回顾一下我学习网络封装的知识。
二、封装过程
1、先把接口的数据格式理清楚,一般返回的Json格式都是这样:
{
"code":200
"msg":"成功"
"results":{[data]
}
}
results里面的才是我们真正需要的实例,但是每个实例都不一样,因此要针对不同的数据来封装。
因此,先创建一个公共接口实例类HttpResult类,我们真正需要的实例先不初始化,用T来代替(例如MovieBean、GoodsBean这样的),我这里数据结构不一样,因此可能和你的不一样:
public class HttpResult<T> {
private int ret;
private String msg;
private String token;
private T result;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRet() {
return ret;
}
public void setRet(int ret) {
this.ret = ret;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
}
2、首先创建一个Retrofit需要的interface,用来获取数据接口
例如:这里有一个仿拉手团购的公开接口,只有两个数据,这样的:
获取团购信息:http://7xij5m.com1.z0.glb.clouddn.com/spRecommend.txt
获取电影信息:http://7xij5m.com1.z0.glb.clouddn.com/filmHot_refresh.txt
因此我的APIService是这样写的:
public interface APIService {
String base_url = "http://7xij5m.com1.z0.glb.clouddn.com/";
@GET("filmHot_refresh.txt")
Observable<HttpResult<List<MovieBean>>> getMovie();
@GET("spRecommend.txt")
Observable<HttpResult<GoodsBean>> getGoods();
}
3、接口弄好了之后,接下来要初始化我们的Retrofit和OKHttp了,用一个HttpRetrofit来封装:
public class HttpRetrofit {
private static final int DEFAULT_TIMEOUT = 10;
private final APIService apiService;
HttpRetrofit(){
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.readTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
builder.retryOnConnectionFailure(true);
Cache cache = new Cache(App.getAppContext().getCacheDir(),10 * 1024 * 1024);
builder.cache(cache);
builder.addInterceptor(new CacheInterceptor());
builder.addNetworkInterceptor(new CacheInterceptor());
apiService = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(APIService.base_url)
.build().create(APIService.class);
}
APIService getApiService(){
return apiService;
}
}
如果你会Retrofit的基本使用,上面的代码应该能看懂,CacheInterceptor我就不贴代码了,就是针对网络状况不同是否用缓存的一个判断。
3、将APIService单例化,用一个HttpFactory类来给外界调用:
public class HttpFactory {
private static APIService apiService;
private HttpFactory(){
}
public static APIService getApiService(){
if(apiService == null){
synchronized (HttpFactory.class){
if(apiService == null){
apiService = new HttpRetrofit().getApiService();
}
}
}
return apiService;
}
}
4、现在,我们已经进行了一个基本的封装,但是每次我们调用的时候都要重复写指定线程的代码,这样很麻烦,能不能一次性把它们封装好,以后调用的时候不用再写这一句代码了呢?答案是当然可以,RxJava为我们提供了一个Transformer类,它的作用是用来将一个Observable转换成另一个Observable,调用的时候使用compose操作符就行,它类似于flapmap操作符,但是效率更高(原因好像是它复用了Observable,这个具体我也不太清楚)。我们写一个HttpResultTransformer类:
public class HttpTransformer<R extends HttpResult<T>, T> implements Observable.Transformer<R,T>{
private ActivityLifecycleProvider provider;
public HttpTransformer(ActivityLifecycleProvider provider){
this.provider = provider;
}
@Override
public Observable<T> call(Observable<R> rObservable) {
return rObservable.flatMap(new Func1<R, Observable<T>>() {
@Override
public Observable<T> call(R r) {
if(r.getRet() != 0){
return createData(r.getResult());
}else {
return Observable.error(new ApiException("网络出错"));
}
}
}).subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.compose(provider.<T>bindUntilEvent(ActivityEvent.PAUSE));
}
private Observable<T> createData(final T data){
return Observable.create(new Observable.OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(data);
subscriber.onCompleted();
}catch (Exception e){
subscriber.onError(e);
}
}
});
}
}
这段代码有点复杂,我来说明一下,首先对我们获取的HttpResult进行一个判断返回码,如果返回码不正确,直接交给Observable去调用Error方法(其中APIException是我自己创建的一个异常类),就不再继续转换和发送请求了;如果返回码正确,那么我们要把HttpResult转换成我们真正需要的result,然后同时指定线程和RxLifecycle的接触绑定的方法,这样,就省略了很多代码。
在没有封装之前,我们写的代码是这样:
private void getGoods(){
HttpFactory.getApiService()
.getGoods()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<HttpResult<GoodsBean>>bindUntilEvent(ActivityEvent.DESTROY))
.subscribe(new Subscriber<HttpResult<GoodsBean>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(HttpResult<GoodsBean> httpResult) {
GoodsBean bean = httpResult.getResult();
}
});
}
封装之后,代码是这样的:
private void getGoods2(){
HttpFactory.getApiService()
.getGoods()
.compose(new HttpTransformer<HttpResult<GoodsBean>,GoodsBean>(this))
.subscribe(new Subscriber<GoodsBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(GoodsBean goodsBean) {
}
});
}
是不是清爽了很多?APIException的代码,我这里还没有写错误码处理:
public class ApiException extends IllegalAccessException {
public ApiException(String s) {
super(s);
}
public ApiException(int resultCode){
this(getApiExceptionMessage(resultCode));
}
private static String getApiExceptionMessage(int code){
switch (code){
default:
}
return null;
}
}
5、最后,在我们访问网络的时候需要弹一个提示框,在访问结束后让提示框消失。
有一个问题要注意,我们如果在Subscriber的onStart里面弹框的话可能会有线程问题,但是RxJava已经为我们提供了解决办法,subscribeOn方法只有在第一次生效,而第二次subscriberOn时候可以让onStart方法在主线程执行(这里有疑问,应该是让doOnSubscriber在主线程执行,但是我在实际中发现onStart也在主线程执行了)。
因此,我们可以在Subscriber的onStart中弹框,在onComplete方法中取消弹框。这里,首先用一个ProgressDialogHander来封装我们的ProgressDialog,
ProgressDialogHander继承Handler,在需要的时候弹框,不需要的时候不弹框(这里有一个 ProgressDialogCannelListener用来做用户主动取消弹框后的操作(一般是取消订阅):
public class ProgressDialogHandler extends Handler {
public static final int SHOW_PROGRESSDIALOG = 1;
public static final int DISMISS_PROGRESSDIALOG = 2;
interface OnProgressDialogCannelListener{
void onCannelProgress();
}
private ProgressDialog mDialog;
private Context mContext;
private OnProgressDialogCannelListener mListener;
private boolean canCannel;
public ProgressDialogHandler(Context context,OnProgressDialogCannelListener listener,boolean canCannel){
this.mContext = context;
this.mListener = listener;
this.canCannel = canCannel;
}
private void initProgressDialog(){
if(mDialog == null){
mDialog = new ProgressDialog(mContext);
mDialog.setCanceledOnTouchOutside(false);
mDialog.setMessage("加载中 ...");
mDialog.setCancelable(canCannel);
if(canCannel){
mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mListener.onCannelProgress();
}
});
}
}
if(!mDialog.isShowing()){
mDialog.show();
}
}
private void dissmissDialog(){
if(mDialog != null){
mDialog.dismiss();
mDialog = null;
}
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case SHOW_PROGRESSDIALOG:
initProgressDialog();
break;
case DISMISS_PROGRESSDIALOG:
dissmissDialog();
break;
}
}
}
再写一个类ProgressSubscriber继承Subscriber,用来给用户回调请求成功或者失败的方法:
public abstract class ProgressSubscriber<T> extends Subscriber<T> implements ProgressDialogHandler.OnProgressDialogCannelListener {
ProgressDialogHandler mHandler;
public ProgressSubscriber(Context context){
mHandler = new ProgressDialogHandler(context,this,true);
}
@Override
public void onStart() {
super.onStart();
showDialog();
}
@Override
public void onCompleted() {
dismissDialog();
}
@Override
public void onError(Throwable e) {
if(false){//TODO 网络不可用
onFailed("网络不可用!");
} else if(e instanceof ApiException){
onFailed(e.getMessage());
} else {
onFailed("请求失败,请稍后再试");
}
}
@Override
public void onNext(T t) {
onSuccess(t);
}
private void showDialog(){
if(mHandler != null){
mHandler.sendEmptyMessage(ProgressDialogHandler.SHOW_PROGRESSDIALOG);
}
}
private void dismissDialog(){
if(mHandler != null){
mHandler.sendEmptyMessage(ProgressDialogHandler.DISMISS_PROGRESSDIALOG);
mHandler = null;
}
}
protected abstract void onSuccess(T t);
protected abstract void onFailed(String message);
@Override
public void onCannelProgress() {
if(!this.isUnsubscribed()){
this.unsubscribe();
}
}
}
至此,这次封装就结束了,我们去请求GoodBeans的操作就是这样了:
private void getGoods2(){
HttpFactory.getApiService()
.getGoods()
.compose(new HttpTransformer<HttpResult<GoodsBean>,GoodsBean>(this))
.subscribe(new ProgressSubscriber<GoodsBean>(this) {
@Override
protected void onSuccess(GoodsBean goodsBean) {
}
@Override
protected void onFailed(String message) {
}
});
}
三、参考博客: