Retrofit+RxAndroid的使用
这一段时间,除了流行的React Native
之外,还有不少流行的框架如Retrofit
,Rx
等等。
Retrofit简介:
在Android API4.4之后,Google官方使用了square公司推出的okHttp替换了HttpClient的请求方式。后来square公司又推出了基于okHttp的网络请求框架:Retrofit。
Retrofit采用注解的方式进行配置相关信息,如地址,请求头,请求方式等等,另外又添加了Gson解析库的接口,接入流行的框架Rx,因为提供了配置基础设置的接口,因此存在良好的扩展性。
Retrofit的基础配置
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
// .addConverterFactory(FastJsonConverterFactory.create())
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(httpClient.addInterceptor(interceptor).addNetworkInterceptor(netInterceptor).build())
.build();
其中,addConverterFactory
方法是用来配置Gson解析库,addCallAdapterFactory
方法是用来添加Rx的适配器,通过client
可以来配置基础内容,如拦截器(配置统一的请求头,获取指定的相应头),缓存配置等等。
通过retrofit.create(Class clazz)
获取网络接口,clazz
为Interface类型,Interface中采用注解的方式去请求网络,方式如下:
GET请求方式:
@Headers("namev:alue")
// @Headers({"key:value","key:value"})
@GET("URL")
Call<ResponseBody> getConnection(@Query("param") String param);
GET请求方式(用来Encode):
@GET("URL")
Call<ResponseBody> getConnection(@QueryMap(encode = true) Map<String, String> param);
POST请求方式(JSON请求体):
@POST("URL")
Call<ResponseBody> getConnection(@Body("param") Object p1);
POST请求方式(键值对):
@POST("URL")
Call<ResponseBody> getConnection(@FieldMap Map<String, String> params);
ResponseBody
是未采用ConverterFactory
的
结果,因此需要通过API手动解析;而采用ConverterFactory
之后,则可以将ResponseBody
自动解析为指定的Object,eg:User:
@GET("URL")
Call<User> getConnection(@Query("param") String param);
因此我们只需要建立java类:
public class HttpUtil{
publi Retrofit mRetrofit
public HttpUtil(String baseUrl, Interceptor interceptor){
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
mRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
// .addConverterFactory(JsonConverterFactory.create())
.client(httpClient.addInterceptor(interceptor).builder())
.build();
}
public <T> T getServer(Class<T> clazz){
return mRetrofit.create(clazz);
}
}
public interface UserServer{
@GET("URL")
Call<ResponseBody> getConnection(@Query("param") String param);
// @GET("URL")
// Call<User> getConnection(@Query("param") String param);
}
Retrofit的基础使用
使用步骤:通过配置addConverterFactory
,我们就能直接通过接口调用得到json解析的返回值,否则会返回Call,需要手动处理网络请求,通过
同步:call.execute().body().string();
异步:call.enqueue(new Callback(){...});
获取网络响应:
ResponseBody response = new HttpUtil("http://baidu.com",new Interceptor(){
@Override
public Response intercept(Chain chain) throws IOException {
return chain.proceed(chain.request());
}
}).create(UserServer.class).getConnection("param");
Rx简介
初步了解Rx
Rx是ReactiveX-响应式编程的简称,是一个函数库或者是开源框架。可以参考ReactiveX了解他的一些基本概念。
接下来Rx的结合使用:
在Retrofit的简介中,我们提到有一个addCallAdapterFactory
的基本配置,是用来做Rx的桥接器使用的。通过该设置,我们的Retrofit代码接口就可以华丽的变身为:
public interface UserServer{
@GET("URL")
Observable<User> getConnection(@Query("param") String param);
}
也就是说,我们顺利的建立了可观测对象(Observables),通过Observables提供的Api,可以对它进行类似于工厂的自动化生产一样的链式操作,大量的减轻了我们的代码编写工作和阅读维护工作。尤其在异步线程的处理和对原数据组合变化上,更是为我们提供了便利的处理机制。
小结:
关于Rx,更多的使用方式在官网上都有对应的API讲解,在这里就不再赘述了,我们只需要记住几个关键点:
1. Observable:可观测对象
2. Operators: 操作符API的总称
3. Scheduler: 线程
Demo讲解
demo地址:RxRetrofitFrameWork
思路详解:
Retrofit的核心还是okhttp,因此很我们对Retrofit的基本设置也其实操作到了okhttp。针对于不同的业务,网络请求大致有几点要求,总结如下:
1. 常用的信息头,如:网络状态、系统类型以及版本、令牌......
2. 缓存类型的设定:如服务器判别式缓存响应,无网络状态响应等等
3. 信息传输方式如Gzip的格式的特殊化处理
4. log日志的打印
如果还有更多的通用要求,欢迎留言。
因此针对以上几点,我们需要配置不同的拦截器去处理这件事情:
通过addInterceptor
添加的Interceptor主要是用于处理请求的,在Response intercept(Chain chain)
方法中,我们能够添加统一信息头;而通过addNetworkInterceptor
添加的Interceptor主要是用于处理由网络请求得到的非缓存结果的响应,也就是说,如果我们得到的是缓存响应,则不会经过由addNetworkInterceptor
添加的Interceptor处理,只会通过addInterceptor
添加的Interceptor处理。另外设置缓存目录和大小,设置响应失败的重试策略,也是根据业务需要去设定。因此,我们将这里的封装做为最底层的支持,提供网络请求器,生成请求句柄:
public class HttpManager {
private Retrofit mRetrofit;
private OkHttpClient mHttpClient;
private NetServer mNetServer;
public HttpManager(String url, Interceptor interceptor, Interceptor netInterceptor){
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cache(new Cache(new File(NetApplication.getInstance().getApplicationContext().getCacheDir(), "caches"), 1024 * 1024 * 100));
mHttpClient = builder.addInterceptor(interceptor).addNetworkInterceptor(netInterceptor).retryOnConnectionFailure(true).build();
mRetrofit = new Retrofit.Builder()
.baseUrl(url)
.client(mHttpClient)
.build();
mNetServer = mRetrofit.create(NetServer.class);
}
public NetServer getServer(){
return mNetServer;
}
}
接下来是业务层的相关配置:
public class NetProxy {
private HttpManager mManger;
private String BASE_URL = "";
public NetProxy(){
mManger = new HttpManager(BASE_URL, new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder();
// HttpUrl originalHttpUrl = original.url();
// String queryString = originalHttpUrl.encodeQuery(); // 获取url中的参数部分
// String path = originalHttpUrl.url().getPath(); // 获取相对地址
// Buffer buffer = new Buffer();
// builder.body().writeTo(buffer);
// String requestContent = buffer.readUtf8(); // 用于post请求获取form表单内容
builder.addHeader("key", "value");
builder.addHeader("Accept-Encoding", "gzip");
Request request = builder.build();
return chain.proceed(request);
}
}, new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
Response.Builder builder = response.newBuilder();
if (response.header("Content-Encoding", "").contains("gzip")){
BufferedSource bufferedSource = Okio.buffer(new GzipSource(response.body().source()));
String temStr = bufferedSource.readUtf8();
bufferedSource.close();
ResponseBody body = ResponseBody.create(MediaType.parse("application/json"), temStr);
builder.body(body);
}else{
BufferedSource bufferedSource = Okio.buffer(response.body().source());
String temStr =bufferedSource.readUtf8();
bufferedSource.close();
ResponseBody body = ResponseBody.create(MediaType.parse("application/json"), temStr);
builder.body(body);
}
return builder.build();
}
});
}
public NetServer getNetServer(){
return mManger.getServer();
}
}
一个商业级别的App通常是有着大量的接口Api的,因此当接口越多时,越难以维护。因此我们可以利用FieldMap
和QueryMap
进行封装处理,统一成一个请求接口,传递不同的参数值,因此我们的NetServer
如下:
public interface NetServer {
@GET
Call<ResponseBody> getRequest(@Url String url, @QueryMap Map<String, String> map);
@FormUrlEncoded
@POST
Call<ResponseBody> postRequest(@Url String url, @QueryMap Map<String, String> queryMap, @FieldMap Map<String, String> postMap);
}
这里没有文件的上传,欢迎提供代码进行补全。
每个Api都有着不同的请求参数长度,请求参数类型和请求地址,因此我们将这些内容整合为动态的参数对象,组成url,map的形式,方便我们接口类型的统一化管理。
现在我们的网络请求句柄创建好了,底层相应的配置也做好了,现在需要开启我们的请求过程。在这里注意,为什么没有选择RxAdapter和JSONConvertFactory,第一是因为如果使用了JSONConvertFactory,我们的返回类型则不是统一的Call<ResponseBody>
,而是指定的Bean;第二是因为很有可能,我们需要在IO线程处理一些信息,如果使用了RxAdapter则我们通过接口就直接创建了一个完整的Observable,不利于代码的添加。
创建监听器和协议:
public interface NetResultCallback<E> {
void onStart();
void onSuccess(E data);
void onFailed(int code, String msg);
void cancel();
}
public class BaseServiceResult<T> {
public int ret;
public String msg;
public T data;
@Override
public String toString() {
return "BaseServiceResult{" +
"ret=" + ret +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
public interface ErrorCode {
int CODE_OK = 0;
int CODE_TIME_OUT = -5000;
int CODE_NET_ERROR = -5001;
int CODE_RESPONSE_EMPTY = -5002;
}
补全网络请求过程:
public void excute(final String mUrl, final Map<String, String> mQueryMap, final Map<String, String> mPostMap, final MethodType mMethodType, final Class mClazz){
Observable.create(new Observable.OnSubscribe<BaseServiceResult<T>>() {
@Override
public void call(Subscriber<? super BaseServiceResult<T>> subscriber) {
Call<ResponseBody> responseBody = new NetProxy(). getNetServer().getRequest(mUrl, mQueryMap);
// Call<ResponseBody> responseBody = new NetProxy().getNetServer().postRequest(mUrl, mPostMap, mQueryMap);
try {
String strJSON = responseBody.execute().body().string();
BaseServiceResult<T> baseServiceResult = JSON.<BaseServiceResult<T>>parseObject(strJSON, BaseServiceResult.class);
if (baseServiceResult.data != null) {
if (baseServiceResult.data instanceof JSONObject) {
baseServiceResult.data = JSON.parseObject(((JSONObject) baseServiceResult.data).toJSONString(), mClazz);
} else if (baseServiceResult.data instanceof JSONArray) {
baseServiceResult.data = (T) JSON.parseArray(((JSONArray) baseServiceResult.data).toJSONString(), mClazz);
}
}
subscriber.onNext(baseServiceResult);
subscriber.onCompleted();
} catch (Exception e) {
e.printStackTrace();
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getSubscrobe());
}
private Subscriber<BaseServiceResult<T>> getSubscrobe(final NetResultCallback<T> mCallback){
Subscriber<BaseServiceResult<T>> mSubscriber = new Subscriber<BaseServiceResult<T>>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (mCallback != null) {
mCallback.onFailed(ErrorCode.CODE_TIME_OUT, NetApplication.getInstance().getString(R.string.connect_time_out));
Log.e(TAG, "onError: " + e.getMessage());
}
}
@Override
public void onNext(BaseServiceResult<T> tBaseServiceResult) {
if (mCallback != null) {
if (tBaseServiceResult == null){
mCallback.onFailed(ErrorCode.CODE_RESPONSE_EMPTY, NetApplication.getInstance().getString(R.string.connect_time_out));
return;
}
Log.i(TAG, "onNext: " + tBaseServiceResult.toString());
if (tBaseServiceResult.ret == ErrorCode.CODE_OK) {
mCallback.onSuccess(tBaseServiceResult.data);
}else{
mCallback.onFailed(tBaseServiceResult.ret, tBaseServiceResult.msg);
}
}
}
};
return mSubscriber;
现在基本流程已经跑通,网络部分的基础已经搭建好,然而这样的执行代码还是有着大量重复的感觉,因此我们需要对执行代码进行二次封装,由上面的方法来看,我们发现就相当于是一次下厨,准备好了原材料如:mUrl,mCallback,mClazz等等,就能够直接执行,因此我们将这些准备和执行视为一个整体,具备了基本属性和基本行为动作,顺便参照我们Rx的链式模式,我们封装如下:
public class NetProcessor<T> {
private NetResultCallback<T> mCallback;
private Map<String, String> mQueryMap;
private Map<String, String> mPostMap;
private String mUrl;
private Class<T> mClazz;
private @MethodType int mMethodType;
private boolean mNeedRetry = true;
private Subscriber<BaseServiceResult<T>> mSubscriber;
private NetServer mServer;
private static final String TAG = "NetProcessor";
public static <T> NetProcessor<T> get(){
NetProcessor<T> netProcessor = new NetProcessor<T>();
return netProcessor;
}
public static <T> NetProcessor<T> post(){
NetProcessor<T> netProcessor = new NetProcessor<T>();
netProcessor.mPostMap = new HashMap<String, String>();
return netProcessor;
}
private NetProcessor(){
mQueryMap = new HashMap<>();
mServer = Controller.getInstance().getmNetProxy().getNetServer();
}
public NetProcessor<T> putParam(String key, String value) {
mQueryMap.put(key, value);
return this;
}
public NetProcessor<T> postParam(String key, String value){
if (mPostMap == null){
synchronized (this){
mPostMap = new HashMap<String, String>();
}
}
mPostMap.put(key, value);
return this;
}
public NetProcessor<T> onQueryMap(Map<String, String> queryMap) {
this.mQueryMap.putAll(queryMap);
return this;
}
public NetProcessor<T> onPostMap(Map<String, String> postMap) {
if (mPostMap == null){
mPostMap.putAll(postMap);
}
return this;
}
public NetProcessor<T> onUrl(String url) {
this.mUrl = url;
return this;
}
public NetProcessor<T> onCallback(NetResultCallback<T> callback) {
this.mCallback = callback;
return this;
}
public NetProcessor<T> onClazz(Class<T> clazz){
this.mClazz = clazz;
return this;
}
public NetProcessor<T> onRetry(boolean needRetry){
this.mNeedRetry = needRetry;
return this;
}
public NetProcessor<T> excute() {
mCallback.onStart();
Observable.create(new Observable.OnSubscribe<BaseServiceResult<T>>() {
@Override
public void call(Subscriber<? super BaseServiceResult<T>> subscriber) {
Call<ResponseBody> responseBody = null;
switch (mMethodType){
case MethodType.METHOD_GET:
responseBody = mServer.getRequest(mUrl, mQueryMap);
break;
case MethodType.METHOD_POST:
responseBody = mServer.postRequest(mUrl, mPostMap, mQueryMap);
break;
}
try {
String strJSON = responseBody.execute().body().string();
BaseServiceResult<T> baseServiceResult = JSON.<BaseServiceResult<T>>parseObject(strJSON, BaseServiceResult.class);
if (baseServiceResult.data != null) {
if (baseServiceResult.data instanceof JSONObject) {
baseServiceResult.data = JSON.parseObject(((JSONObject) baseServiceResult.data).toJSONString(), mClazz);
} else if (baseServiceResult.data instanceof JSONArray) {
baseServiceResult.data = (T) JSON.parseArray(((JSONArray) baseServiceResult.data).toJSONString(), mClazz);
}
}
subscriber.onNext(baseServiceResult);
subscriber.onCompleted();
} catch (Exception e) {
e.printStackTrace();
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable.flatMap(new Func1<Throwable, Observable<?>>() {
@Override public Observable<?> call(Throwable error) {
if (mNeedRetry) {
mNeedRetry = false;
// For IOExceptions, we retry
if (error instanceof IOException) {
return Observable.just(null);
}
}
// For anything else, don't retry
return Observable.error(error);
}
});
}
})
.subscribe(getSubscrobe());
return this;
}
public void cancel(){
if (mSubscriber != null && !mSubscriber.isUnsubscribed()){
mSubscriber.unsubscribe();
mCallback.cancel();
}
}
private Subscriber<BaseServiceResult<T>> getSubscrobe(){
mSubscriber = new Subscriber<BaseServiceResult<T>>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (mCallback != null) {
mCallback.onFailed(ErrorCode.CODE_TIME_OUT, NetApplication.getInstance().getString(R.string.connect_time_out));
Log.e(TAG, "onError: " + e.getMessage());
}
}
@Override
public void onNext(BaseServiceResult<T> tBaseServiceResult) {
if (mCallback != null) {
if (tBaseServiceResult == null){
mCallback.onFailed(ErrorCode.CODE_RESPONSE_EMPTY, NetApplication.getInstance().getString(R.string.connect_time_out));
return;
}
Log.i(TAG, "onNext: " + tBaseServiceResult.toString());
if (tBaseServiceResult.ret == ErrorCode.CODE_OK) {
mCallback.onSuccess(tBaseServiceResult.data);
}else{
mCallback.onFailed(tBaseServiceResult.ret, tBaseServiceResult.msg);
}
}
}
};
return mSubscriber;
}
@IntDef({MethodType.METHOD_GET, MethodType.METHOD_POST})
public @interface MethodType {
// GET请求
int METHOD_GET = 0;
// POST请求
int METHOD_POST = 1;
}
private String getQuertString(){
if (mQueryMap == null){
return "";
}
List<QueryString> list = new ArrayList<>();
QueryString query = null;
Set<String> set = mQueryMap.keySet();
StringBuilder queryBuilder = new StringBuilder();
for (String key : set) {
query = new QueryString(key, mQueryMap.get(key));
list.add(query);
}
Collections.sort(list);
int size = list.size();
try {
for (int i = 0; i < size; i++) {
queryBuilder.append(list.get(i).toStringAddAnd( i == 0 ? false : true));
}
} catch (Exception e) {
e.printStackTrace();
}
return queryBuilder.toString();
}
private String getPostString(){
if (mPostMap == null){
return "";
}
Set<String> set = mPostMap.keySet();
StringBuilder postBuilder = new StringBuilder();
for (String key : set) {
try {
postBuilder.append("&").append(key).append("=").append(URLEncoder.encode(mPostMap.get(key), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
postBuilder.deleteCharAt(0);
return postBuilder.toString();
}
}
应用如下:
public class UserBeanModel {
public static NetProcessor getUser(NetResultCallback<UserBean> callback){
Map<String, String> params = new HashMap<>();
params.put("name", "username");
params.put("psw", "userpsw");
return NetProcessor.<UserBean>get()
.onCallback(callback)
.onRetry(true)
.onClazz(UserBean.class)
.onUrl("/get/user")
.onQueryMap(params)
.excute();
}
}
最后通过 UserBeanModel.getUser(new NetResultCallback())
执行就能得到相应的结果。
关于RxJava2和XML的结合,详见Retrofit接入RxJava2的使用以及XML在Retrofit中的使用。