Android之Retrofit和RxJava的结合使用
转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/53674161
本文出自:【顾林海的博客】
##前言
Retrofit,一个时尚的代名词,好像不知道Retrofit就不算Android开发工程师了,因此我也来时尚一把,写这篇文章旨在使广大开发者能根据这篇浅薄的文章来了解Retrofit,并将它用到我们的项目中去,当然Retrofit和RxJava结合起来用是非常酸爽的。文章开头会先去介绍Retrofit,并单独使用Retrofit,后面会将Retrofit和RxJava结合起来使用,最后会封装一个Retrofit和RxJava结合的请求框架。
##Retrofit介绍
在漫长的时间里,Retrofit经历了从1.x到2.1(最新版请参看这里“https://github.com/square/retrofit”),相比retrofit1.x来说,retrofit2.x更新了几个不错的功能点。Retrofit出自Square公司,是一个类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 ,当然OkHttp也是出自这家公司。
##实例一(单独使用Retrofit )
该实例只会讲解单纯使用 Retrofit的用法,源码位于底下github地址,实例一位于demo1包下。
使用 Retrofit 前我们需要做以下两件事:
- 在app/build.gradle 中引入 Retrofit。
- 在AndroidManifest中添加网络请求权限。
在gradle中引入 Retrofit:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
在AndroidManifest中添加网络请求权限:
下面正式我们的Retrofit使用之旅。
###步骤一:创建我们的请求API(Service)
这里的请求API其实就是我们的向服务端请求的接口地址。Retrofit2.x在定义Service时,已经不区分同步和异步之分了。可以直接看下面的代码,建议大家边看文章边动手撸下代码,代码中的接口地址替换成你们公司或是自己的服务器的地址。在Retrofit中使用注解的方式来区分请求类型.比如@GET("")表示一个GET请求,括号中的内容为请求的地址.
public interface APIService { @GET("getBrandBanner.php") Call getBanner(@Query("uid") String _uid, @Query("token") String _token); @GET("getHomePager.php") Call getHomePager(); @FormUrlEncoded @POST("editUserInfo.php") Call postIP(@Field("name") String name, @Field("age") int age);}
以上定义了两个请求方式,分别是Get和Post请求,其中Get请求分为无参和有参请求。至此接口地址已经创建完毕。 ###步骤二:创建Retrofit实例
在请求接口的API定义完毕后,就需要使用Retrofit Builder类,来指定Service的baseUrl(也就是域名)。
在Activity中编写代码:
private static final String API_URL = "http://n1.glh.la/apps/";Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();APIService apiService = retrofit.create(APIService.class);Call responseBodyCall = apiService.getBanner("10915", "585234059ab68");
创建完Retrofit实例后,通过该实例创建APIService,接着通过APIService中定义的方法来获取Call对象。前置工作已经准备完毕,剩下的就是进行请求。 ###步骤三:请求(异步与同步) 一、同步请求
同步请求使用execute方法,但不能在UI线程中执行,否则会阻塞UI线程,引起NetwordOnMainThreadException异常。因此,这里使用handler+thread的方式来进行请求,在子线程中请求数据,并通过handler来刷新界面。
handler:
private static class MyHandler extends Handler { WeakReference weakReference; public MyHandler(DemoActivity1 activity) { weakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { DemoActivity1 activity = weakReference.get(); if (activity != null) { String json = (String) msg.obj; activity.setData(json); } super.handleMessage(msg); }}
Thead:
private static class MyThread extends Thread { private MyHandler myHandler; Call bodyResponse; public MyThread(Call responseBodyCall, MyHandler handler) { bodyResponse = responseBodyCall; myHandler = handler; } @Override public void run() { super.run(); try { Response body = bodyResponse.execute(); Message message = new Message(); message.obj = body.body().string(); myHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } }}
使用:
/** * 同步 * * @param responseBodyCall */private void synSendRequest(Call responseBodyCall) { MyHandler myHandler = new MyHandler(this); MyThread myThread = new MyThread(responseBodyCall, myHandler); myThread.start();}private void setData(String json) { tv_show.setText(json);}
同步请求的整体流程大致就这样了。
二、异步请求相比同步请求方式,异步比较简单,因此建议使用异步请求方式,异步请求方式使用enqueue方法,并通过回调Callback 泛型接口的两个方法:
/** * 异步 * * @param responseBodyCall */private void asySendRequest(Call responseBodyCall) { responseBodyCall.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { try { tv_show.setText(response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call call, Throwable t) { tv_show.setText("error"); } });}
###其它一些注意事项
service 的模式变成Call的形式的原因是为了让正在进行的事务可以被取消。要做到这点,你只需调用call.cancel()。
##实例二(Retrofit 与 Gson)
当然如果你想把json字符串解析成DAO(实体类对象),在Retrofit2.x中,Converter不再包含其中,因此需要我们把Gson Converter依赖添加进来,此实例源码在源文件中的demo2包下。
添加Gson Converter:
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
demo1下的程序进行修改如下: ###修改一:接口请求 public interface APIService { @GET("getBrandBanner.php") Call getBanner(@Query("uid") String _uid, @Query("token") String _token);}
在定义接口请求时,我们传入了一个HttpResult类,它是我们从服务器返回的json串解析后的实体类。
###修改二:创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).addConverterFactory(GsonConverterFactory.create()).build();APIService apiService = retrofit.create(APIService.class);Call responseBodyCall = apiService.getBanner("10915", "58524bb42c9ca");
我们在通过Retrofit Builder类来构造Retrofit实例时插入了一个Converter(Gson Converter)。 ###修改三:请求 ####同步请求 /** * 同步 * * @param responseBodyCall */private void synSendRequest(Call responseBodyCall) { MyHandler myHandler = new MyHandler(this); MyThread myThread = new MyThread(responseBodyCall, myHandler); myThread.start();}private void setData(String data) { tv_show.setText(data);}private static class MyHandler extends Handler { WeakReference weakReference; public MyHandler(DemoActivity2 activity) { weakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { DemoActivity2 activity = weakReference.get(); if (activity != null) { HttpResult hotBean = (HttpResult) msg.obj; if (hotBean != null) { activity.setData(hotBean.data.pc.number); } } super.handleMessage(msg); }}private static class MyThread extends Thread { private MyHandler myHandler; Call bodyResponse; public MyThread(Call responseBodyCall, MyHandler handler) { bodyResponse = responseBodyCall; myHandler = handler; } @Override public void run() { super.run(); try { Response body = bodyResponse.execute(); Message message = new Message(); message.obj = body.body(); myHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } }}
####异步请求
/** * 异步 * * @param responseBodyCall */private void asySendRequest(Call responseBodyCall) { responseBodyCall.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { HotBean hotBean = response.body().data; tv_show.setText(hotBean.pc.number); } @Override public void onFailure(Call call, Throwable t) { tv_show.setText("error"); } });}
##实例三(Retrofit与RxJava ) 经历了上面的两个例子,大家对Retrofit的使用已经有了充分的认识了吧,如果不满足于此,可以继续看下去,因为高潮马上来了,接下来会讲解Retrofit与RxJava的结合使用。
###RxJava介绍想来想去对RxJava介绍的文章,网上多的是,当然我认为这篇文章《给Android开发者的RxJava讲解》("http://gank.io/post/560e15be2dca930e00da1083")还是很不错,所以啊,我就不介绍了,哈哈哈。。。。。,强烈建议大家将RxJava讲解这篇文章看看,当然,不看也没问题,除非你只是拿来就用,否则作为一个有"节操"的程序员,还是老老实实的研究下。
###正题Retrofit2.x中有个机制,叫做CallAdapter,而Retrofit团队已经提供了RxJava的CallAdapter,这时就需要在app/build.gradle中引入以下依赖:
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'compile 'io.reactivex:rxjava:1.1.0'compile 'io.reactivex:rxandroid:1.1.0'
在之前例子中,为了使用同步请求方式,需要自己创建线程,过程比较繁琐,所以只能使用异步方式,基于此,使用RxJava来解决异步的问题,代码实例在demo3包下。
###步骤一:请求接口
在创建请求接口时,我们就可以将Service作为Observable返回:
public interface APIService { @GET("getBrandBanner.php") Observable getBanner(@Query("uid") String _uid, @Query("token") String _token);}
###步骤二:创建Retrofit实例
//step1Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();APIService apiService = retrofit.create(APIService.class);Observable httpResultObservable = apiService.getBanner("10915", "58524bb42c9ca");
使用CallAdapter这种机制,可以在 Retrofit Builder 链中调用addCallAdapterFactory方法。 ###步骤三:请求
httpResultObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber() { @Override public void onCompleted() { tv_show.append("请求结束"); } @Override public void onError(Throwable e) { tv_show.append("请求错误"); } @Override public void onNext(HttpResult httpResult) { tv_show.append(httpResult.data.pc.number); } @Override public void onStart() { tv_show.append("请求开始"); }});
上面指定subscribe发生在IO线程中,而指定的Subscriber的回调发生在Android的UI线程中。 呼~~~~,终于把上面的例子写完了,天已经黑了,看看还有什么要讲的,好吧,每次这样请求无意间代码量就增多了,而我们理想中的请求方式应该是这样的,在Activity中是这样的请求的:
private void getNews() { MainHttpRequest.getInstance().getBanner(new ListenerSubscriber>(getNewsListener, DemoActivity4.this));}
接着通过回调获取我们想要的数据: private OnFunctionListener getNewsListener = new OnFunctionListener>() { @Override public void success(ArrayList o) { tv_show.setText(o.get(0).lname); } @Override public void fail(String message) { }};
并且服务端返回的信息,我们应该是抽取其中有用的信息,而code 、message、success、client等等信息不是我们应该关心的,就拿下面的json串来说: { "code":"200", "message":"数据返回成功", "success":true, "data":[ { "id":"1", "lname":"香水合集 | 该换上适合秋天的味道啦~" }, { "id":"3", "lname":"IOPE水滢多效气垫腮红" }, { "id":"0", "lname":"单品小记∣毛孔收收收?痘痘消消消?" }, { "id":"4", "lname":"雅诗兰黛肌透修护精萃蜜" } ]}
我们应该是关心data节点下的json串,因此,不管是为了省代码量还是获取数据方便,我们都有必要对这些进行一定量的封装。 ##实例四:封装 还是拿上面的json串来说事,在这json串中我们业务层应该是只关心data节点下的数据,因此定义一个HttpResult类:
public class HttpResult { private int code; private String message; private boolean success; private T data; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; }}
接着自定义一个Func1的类,用于变换时获取data部分的数据: /** * 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber * * @param Subscriber真正需要的数据类型,也就是Data部分的数据类型 */public class HttpResultFunc implements Func1, T> { @Override public T call(HttpResult httpResult) { if (httpResult.isSuccess()) { Log.e("TAG", "response error"); } return httpResult.getData(); }}
自定义一个Converter类用于Gson解析: public class ResponseConvertFactory extends Converter.Factory { private final Gson gson; public static ResponseConvertFactory create() { return create(new Gson()); } public static ResponseConvertFactory create(Gson gson) { return new ResponseConvertFactory(gson); } private ResponseConvertFactory(Gson gson) { if (gson == null) { throw new NullPointerException("gson == null"); } this.gson = gson; } @Override public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new GsonResponseBodyConverter<>(gson, type); }}
``` class GsonResponseBodyConverter implements Converter }
这样定义后,我们可以在底层根据返回数据的一些参数来判别一些错误类型,方便处理。创建一个单例的Http类:
public class Http {
public static final String BASE_URL = "http://n1.glh.la/apps_T1/";private static final int DEFAULT_TIMEOUT = 5;private Retrofit retrofit;//构造方法私有private Http() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); retrofit = new Retrofit.Builder() .client(builder.build()) .addConverterFactory(ResponseConvertFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build();}private static class SingletonHolder { private static final Http INSTANCE = new Http();}public Retrofit getRetrofit() { return retrofit;}public static Http getInstance() { return SingletonHolder.INSTANCE;}public void getData(Observable o, Subscriber s) { o.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(s);}
}
最后自定义一个Subscriber类,用于请求监听,比如在这里显示一个加载框或者是对一些错误码的处理:
public class ListenerSubscriber extends Subscriber {
private OnFunctionListener mSubscriberOnNextListener;private Context context;public ListenerSubscriber(OnFunctionListener mSubscriberOnNextListener, Context context) { this.mSubscriberOnNextListener = mSubscriberOnNextListener; this.context = context;}@Overridepublic void onStart() {}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) { if (mSubscriberOnNextListener != null) { mSubscriberOnNextListener.fail("error"); }}/** * 将onNext方法中的返回结果交给Activity或Fragment自己处理 * * @param t 创建Subscriber时的泛型类型 */@Overridepublic void onNext(T t) { if (mSubscriberOnNextListener != null) { mSubscriberOnNextListener.success(t); }}
}
public interface OnFunctionListener {
void success(T t);
void fail(String message);
}
整体封装完毕后,再在业务层将上面请求的方式再次进行封装一遍:
/**
- 首页相关请求
- Created by glh on 2016-12-15.
*/
public interface HomeCarouselService {
@GET(“getHomeCarousel.php”)
ObservablegetNews();
}
/**
-
首页相关的网络请求
-
Created by glh on 2016-12-14.
*/
public class MainHttpRequest {private HomeCarouselService mHomeCarouselService;
private Http mHttpMethods;private MainHttpRequest() {
mHttpMethods = Http.getInstance();
mHomeCarouselService = mHttpMethods.getRetrofit().create(HomeCarouselService.class);
}private static class SingletonHolder {
private static final MainHttpRequest INSTANCE = new MainHttpRequest();
}//获取单例
public static MainHttpRequest getInstance() {
return SingletonHolder.INSTANCE;
}/**
- 获取广告
- @param subscriber 由调用者传过来的观察者对象
*/
public void getBanner(Subscriber subscriber) {
Observable observable = mHomeCarouselService.getNews()
.map(new HttpResultFunc());
mHttpMethods.getData(observable, subscriber);
}
}
创建实体类:
public class NewsBean {
public String id;
public String lname;
public String tid;
public String imgurl;
public String desc;
public String ptype;
public String url;
}
完整源码在源文件的demo4中。
##项目下载地址
> 以下是完整的github项目地址,欢迎多多star和fork。> github项目源码地址:[点击【项目源码】](https://github.com/LinhaiGu/Retrofit_RxJava)
更多相关文章
- Android中的Activity之间传递自定义类型的List的方法
- Android(安卓)四种加载方式详解(standard singleTop singleTask s
- AIDL入门
- android 使用服务读取串口数据实例
- android按键驱动开发实例1(修改一个按键)
- android利用post方式与web服务器通信
- 【Android(安卓)开发】 : Activity之间传递数据的几种方式
- android中的5个布局方式
- Android(安卓)NFC源码浅析