您的位置:首页 > 编程语言 > Java开发

Retrofit+RxJava的使用

2016-09-26 17:32 309 查看

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中的使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息