一,概述
Android移动端开发网络框架有很多种,最常用的有Volley、OkHttp、Retrofit(Retrofit底层也是默认使用OkHttp实现)。Volley之前有分析过(Android Volley 简单分析),接下来就来看下OkHttp,Retrofit的使用,以及使用OkHttp、Retrofit、RxJava对网络请求做第二次封装。
二,OkHttp
OkHttp是Android端最火热的轻量级网络请求框架。
2.1,网络请求的简单介绍
每次网络请求都是一次数据交互的过程:客户端和服务端建立连接->客户端发送请求都服务端->服务器应答内部做一些列的处理再把数据返回给客户端->客户端接收到数据断开连接。这是最简单的一次网络数据交互请求。作为客户端就两件事第一件就是封装网络请求Request,第二件就是解析网络请求Response。
网络请求Request由三个部分组成:
- 请求行:包括请求类型(GET、POST、PUT、DELETE、OPTION、PATCH、TRACE、CONNECT)、请求url、HTTP协议以及版本。
- 请求头:网络请求过程中制定的一些约束,键值对的形式表现,每个key都有它特定的意义(比如key:Content-Type约定请求和响应的HTTP body内容编码类型、key:Accept约定请求接受的媒体类型),
- 请求体body:有些请求类型是没有网络请求体比如DELETE、GET、OPTIONS等等。请求体和请求头里面Content-Type要一一对应,客户端发送请求到服务端的时候服务端会按照请求头里面的Content-Type类型来解码请求体body内容。
注:Content-Type用于约束HTTP请求body和应答body的编码格式,常见的Content-Type类型有:application/x-www-form-urlencoded :body被编码成键值对的形式(表单提交)、multipart/form-data:body被编码成二进制的形式(多用于文件上传) 、application/json:body被编码成json字符串形式。
(个人觉得对用GET请求,Content-Type没啥大的用处,如果服务端没有检查的时候可以不传递)
网络应答Response也是由三个部分组成:
- 响应行:包括两个部分:HTTP协议类型以及版本、HTTP状态码以及状态描述。
- 响应头:和请求头类似也是以键值对的形式出现,里面会放服务端要告诉客户端的一些信息,比如缓存相关一些列的规则(key:Cache-Control),响应体body的编码格式(key:Content-Type)。
- 响应体body:这个才是我们每次网络请求最终要得到的干货(解析要根据响应头里面Content-Type设置的格式)。
2.2,OkHttp里面一些常用的类,api
上面简单的了解了下网络请求和应答的组成部分。接下来咱们就来看下OkHttp使用过程中接触最多的类:OkHttpClient、Call、Request、RequestBody、Response、ResponseBody、Interceptor、CookieJar。
- OkHttpClient:设置一些统一的参数,比如超时设置、拦截器的添加等等。OkHttpClient使用的是建造者模式,那得瞧下OkHttpClient.Builder都有那些public方法了。
/**
* 设置connect超时时间
*/
public OkHttpClient.Builder connectTimeout(long timeout, TimeUnit unit);
/**
* 数据read超时时间
*/
public OkHttpClient.Builder readTimeout(long timeout, TimeUnit unit);
/**
* 数据write超时时间
*/
public OkHttpClient.Builder writeTimeout(long timeout, TimeUnit unit);
/**
* 设置HTTP代理连接(通过别的地址去上网)
*/
public OkHttpClient.Builder proxy(Proxy proxy);
/**
* 设置HTTP的自动代理选择器(有时候需要根据不同的网址旋转不同的代理)
*/
public OkHttpClient.Builder proxySelector(ProxySelector proxySelector);
/**
* 设置cookie持久化的处理
*/
public OkHttpClient.Builder cookieJar(CookieJar cookieJar);
/**
* 设置缓存
*/
public OkHttpClient.Builder cache(Cache cache);
/**
* 设置DNS解析服务器
*/
public OkHttpClient.Builder dns(Dns dns);
/**
* SocketFactory创建socket的工厂类,可以自己去实现(咱们一般很少会去改变这个)
*/
public OkHttpClient.Builder socketFactory(SocketFactory socketFactory);
/**
* SSLSocketFactory创建加密socket的工厂类(咱们一般很少会去改变这个)https时候用到
*/
public OkHttpClient.Builder sslSocketFactory(SSLSocketFactory sslSocketFactory);
/**
* X509TrustManager
*/
public OkHttpClient.Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager);
/**
* 设置服务器验证我们访问的IP地址方式
*/
public OkHttpClient.Builder hostnameVerifier(HostnameVerifier hostnameVerifier);
/**
* 设置那些证书是可以信任的
*/
public OkHttpClient.Builder certificatePinner(CertificatePinner certificatePinner);
/**
* http值401的时候,会调用设置类里面的方法(比如token过期之类的,然后咱们可以在这里面重新设置新的token)
*/
public OkHttpClient.Builder authenticator(Authenticator authenticator);
/**
* 代理服务器需要确认身份
*/
public OkHttpClient.Builder proxyAuthenticator(Authenticator proxyAuthenticator);
/**
* 自定义链接池线程数量啥的,
*/
public OkHttpClient.Builder connectionPool(ConnectionPool connectionPool);
/**
* 是否允许https重定向
*/
public OkHttpClient.Builder followSslRedirects(boolean followProtocolRedirects);
/**
* 是否允许http重定向
*/
public OkHttpClient.Builder followRedirects(boolean followRedirects);
/**
* 是否自动重连
*/
public OkHttpClient.Builder retryOnConnectionFailure(boolean retryOnConnectionFailure);
/**
* 异步任务的Call会放到Dispatcher调度器里面去处理,咱们可以自己去处理调度的逻辑
*/
public OkHttpClient.Builder dispatcher(Dispatcher dispatcher);
/**
* OkHttp支持的协议类型
*/
public OkHttpClient.Builder protocols(List<Protocol> protocols);
/**
* HTTPS具体的安全与连接的决定是由ConnectionSpec接口实现(可以用一组自定义TLS版本和密码套件建立自己的连接规格)
*/
public OkHttpClient.Builder connectionSpecs(List<ConnectionSpec> connectionSpecs);
/**
* 获取应用拦截器
*/
public List<Interceptor> interceptors();
/**
* 添加应用拦截器
*/
public OkHttpClient.Builder addInterceptor(Interceptor interceptor);
/**
* 获取网络拦截器
*/
public List<Interceptor> networkInterceptors();
/**
* 添加网络拦截器
*/
public OkHttpClient.Builder addNetworkInterceptor(Interceptor interceptor);
OkHttpClient相对于OkHttpClient.Builder来说最常用的就多了两个比较重要的方法。
/**
* 通过传入网络请求的封装,返回一个Call调用,通过Call来发起或者取消网络请求。
* @param request 网络请求的封装
* @return 返回一个Call调用
*/
public Call newCall(Request request);
/**
* 获取该OkHttpClient对应的Builder(为了保证OkHttpClient在整个应用中共用一个连接池和缓存)
* @return OkHttpClient.Builder
*/
public OkHttpClient.Builder newBuilder();
- Request:OkHttp网络请求的封装,包含 url,method,headers,body,CacheControl ,tag。发送给服务器的时候会按照HTTP协议要求的格式组成对应的报文发送。
/**
* 设置当前request的url
*/
public Request.Builder url(HttpUrl url);
/**
* 设置当前request的url
*/
public Request.Builder url(String url);
/**
* 设置当前request的url
*/
public Request.Builder url(URL url);
/**
* 添加当前请求header(key-value的形式如果当前header的key存在则会替换)
*/
public Request.Builder header(String name, String value);
/**
* 添加当前请求header(允许存在多个相同的key)
*/
public Request.Builder addHeader(String name, String value);
/**
* 移除当前请求header
*/
public Request.Builder removeHeader(String name);
/**
* 设置请求头(之前设置的无效)
*/
public Request.Builder headers(Headers headers);
/**
* 设置cache相关的请求头(Cache-Control)
*/
public Request.Builder cacheControl(CacheControl cacheControl);
/**
* GET请求
*/
public Request.Builder get();
/**
* HEAD请求
*/
public Request.Builder head();
/**
* POST请求+BODY
*/
public Request.Builder post(RequestBody body);
/**
* DELETE请求+BODY
*/
public Request.Builder delete(RequestBody body);
/**
* DELETE请求+无BODY
*/
public Request.Builder delete();
/**
* PUT请求+BODY
*/
public Request.Builder put(RequestBody body);
/**
* PATCH请求+BODY
*/
public Request.Builder patch(RequestBody body);
/**
* 自己写请求方式+BODY
*/
public Request.Builder method(String method, RequestBody body);
/**
* 设置当前请求的tag
*/
public Request.Builder tag(Object tag);
- RequestBody:用来组装Request请求的body。有些请求的Request是需要body(POST)。
/**
* 告诉body的编码格式,和请求头里面的Content-Type对应
*/
public abstract MediaType contentType();
/**
* 告诉body的长度
*/
public long contentLength() throws IOException {
return -1;
}
/**
* 通过BufferedSink把body具体的内容写入
*/
public abstract void writeTo(BufferedSink sink) throws IOException;
/**
* static 函数返回RequestBody,body是字符串的形式
*/
public static RequestBody create(MediaType contentType, String content);
/**
* static 函数返回RequestBody,body是ByteString形式
*/
public static RequestBody create(final MediaType contentType, final ByteString content);
/**
* static 函数返回RequestBody,body是byte[]形式
*/
public static RequestBody create(final MediaType contentType, final byte[] content);
/**
* static 函数返回RequestBody,body是byte[]形式,多了个byte[]的起始位置和写入的长度
*/
public static RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount);
/**
* static 函数返回RequestBody,body的内容是File
*/
public static RequestBody create(final MediaType contentType, final File file);
RequestBody有两个经常用到的子类FormBody(RequestBody是表单的形式,键值对的形式。通过FormBody.Builder来构造)、MultipartBody(RequestBody包含多段数据,多用于文件的上传通过MultipartBody.Builder来构造)。
- Call:OkHttp网络请求的执行类,或者请求的动作说由他发起。里面的api也非常的简单。
/**
* 获取request(网络请求参数的封装)
*/
Request request();
/**
* 同步请求
* @throws IllegalStateException 当这个Call已经在请求的时候抛出IllegalStateException
*/
Response execute() throws IOException;
/**
* 异步请求数据
* @throws IllegalStateException 当这个Call已经在请求的时候抛出IllegalStateException
*/
void enqueue(Callback responseCallback);
/**
* 取消当前请求
*/
void cancel();
/**
* 网络请求是否在进行(可以在请求之前判断下)
*/
boolean isExecuted();
/**
* 网络请求是否取消
*/
boolean isCanceled();
- Response:OkHttp网络应答的封装。网络请求获取到服务器应答的内容或者缓存的内容之后会解析到Response里面去。Response里面比较重要的应该是Headers、ResponseBody。
/**
* 获取当前请求的request
*/
public Request request();
/**
* 获取当前请求的协议例如{@link Protocol#HTTP_1_1} or {@link Protocol#HTTP_1_0}.
*/
public Protocol protocol();
/**
* 获取当前请求的状态码
*/
public int code();
/**
* 当前请求是否成功[状态码200..300)
*/
public boolean isSuccessful();
/**
* 当前请求状态码对应的message
*/
public String message();
/**
* 获取当前请求SSL/TLS握手协议验证时的信息
*/
public Handshake handshake();
/**
* 获取当前请求返回的header
*/
public List<String> headers(String name);
/**
* 获取当前请求返回的header
*/
public String header(String name);
/**
* 获取当前请求返回的header(如果不存在返回默认值)
*/
public String header(String name, String defaultValue);
/**
* 取出当前请求返回的body(设置字节的长度)
*/
public ResponseBody peekBody(long byteCount) throws IOException;
/**
* 取出当前请求返回的body
*/
public ResponseBody body();
/**
* 是否重定向了
*/
public boolean isRedirect();
/**
* 网络返回的原声数据(如果未使用网络,则为null)
*/
public Response networkResponse();
/**
* 从cache中读取的网络原生数据
*/
public Response cacheResponse();
/**
* 网络重定向后的,存储的上一次网络请求返回的数据
*/
public Response priorResponse();
/**
* 获得所有当前response所有支持的认证
*/
public List<Challenge> challenges();
/**
* response里面header Cache-Control 里面的信息
*/
public CacheControl cacheControl();
/**
* 发起请求的时间
*/
public long sentRequestAtMillis();
/**
* 收到返回数据时的时间
*/
public long receivedResponseAtMillis();
- ResponseBody:Response里面的body数据封装。
/**
* response对应body的Content-Type,response(body的编码类型)
*/
public abstract MediaType contentType();
/**
* response对应body的长度
*/
public abstract long contentLength();
/**
* 获取response对应body的输入流用来读取数据流
*/
public final InputStream byteStream();
/**
* 获取response对应body的BufferedSource(类似于InputStream的输入流用来读取数据)
*/
public abstract BufferedSource source();
/**
* 获取response对应body的byte数组
*/
public final byte[] bytes() throws IOException;
/**
* 获取response对应body的Reader(所有字符输入流的父类)
*/
public final Reader charStream();
/**
* 获取response对应body的String
*/
public final String string() throws IOException;
/**
* 关闭输入流
*/
public void close();
/**
* static函数返回ResponseBody,数据源是String
*/
public static ResponseBody create(MediaType contentType, String content);
/**
* static函数返回ResponseBody,数据源是byte数组
*/
public static ResponseBody create(final MediaType contentType, byte[] content);
/**
* static函数返回ResponseBody,数据源是BufferedSource
*/
public static ResponseBody create(final MediaType contentType, final long contentLength, final BufferedSource content);
- Interceptor:拦截器是OkHttp的精髓所在,是一种能够监控,重写,重试调用的强大机制。拦截器在OkHttp里面是以链式的形式调用的。在intercept()函数里面先charin.request()得到Request做网络请求之前的处理,然后char.proceed(request) 得到Response做网络请求之后的处理。OkHttp里面拦截器两种:应用拦截器、网络拦截器。按照我的理解应用拦截器提供给我们设置一些全局的处理比如网络请求之前我们先判断网络是否连接等等,应用拦截器是每次请求都会被调用到并且只会调用一次。网络拦截器确不同,当从缓存里面拿到数据的时候网络拦截器都不会被调用到,重连的情况网络拦截器可能调用多次。当有些错误要重连的时候就可以用网络拦截器来实现。
/**
* 自定义拦截器的时候需要重写的方法
* 1. 调用charin.request() 得到Request做网络请求之前的处理。
* 2. char.proceed(request) 得到Response
* 3. 得到Response做网络请求之后的处理,把Response返回
*/
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable
Connection connection();
}
- CookieJar:OkHttp提供给我们处理cookie信息的类,里面就两个方法,一个用来保存url对应的Cookie,一个用来获取url对应的Cookie。会通过BridgeInterceptor拦截器把Cookie加到对应Request的请求头里去。
2.3,OkHttp简单使用
啰里啰嗦说了一大堆的各个类的api,都是为使用OkHttp做铺垫。OkHttp的简单使用分为五个步骤。
- OkHttpClient全局参数的设置(每个应用最好使用一个OkHttpClient实例以减少一些缓存和线程的开支,如果OkHttpClient有变化的时候使用newBuilder()来重新生成OkHttpClient对象)。
- Request对象的封装,url、method、编码类型、RequestBody的选择。
- OkHttpClient调用newCall()函数,参数是Request的到Call对象。
- Call对象执行网络请求,有异步同步两种方式。
- 得到网络应答Response解析出我们需要的结果。
OkHttp最简单的一个post请求(body参数是表单的形式)
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder().add("id", "1")
.build();
Request request = new Request.Builder()
.url("http://192.168.5.14:3033")
.post(body)
.build();
client.newCall(request).enqueue(new Callback(){
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
//TODO:response.body()
}
});
OkHttp最简单的一次get请求(参数在url里面)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://192.168.5.14:3033/books?id=1")
.get()
.build();
client.newCall(request).enqueue(new Callback(){
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
//TODO:response.body()
}
});
三,Retrofit
Retrofit官网就一句话介绍(Retrofit turns your HTTP API into a Java interface.)通过注解的方式来实现HTTP API。Retrofit底层对网络的访问默认也是使用OkHttp,Retrofit非常适合于restful url格式的请求。
3.1,Retrofit里面注解
method annotation (方法注解,写在方法前面的注解)
名称 含义 @GET 表明HTTP请求为GET请求 @POST 表明HTTP请求为POST请求 @PUT 表明HTTP请求为PUT请求 @DELETE 表明HTTP请求为DELETE请求 @HEAD 表明HTTP请求为DELETE请求 @OPTIONS 表明HTTP请求为OPTIONS请求 @HTTP 通过@HTTP注解指定http协议的请求方法,路径,是否允许body。前面几种都可以 用这个实现 @PATCH 表明HTTP请求为PATCH请求 @Headers 静态添加请求头(添加固定的请求头) @Multipart 表明发起HTTP请求的RequestBody是Multipar方式 @FormUrlEncoded 表明发起HTTP请求的RequestBody是form表单方式 parameter annotation(参数注解,写在参数里面的注解)
名称 含义 @Header 动态添加请求Header @HeaderMap 以map形式传入的多个header键值对 @Body HTTP请求的body @Field 表明此参数用作HTTP请求的form表单参数 @FieldMap 以map形式传入的form表单参数 @Part 表明参数为Http的multipart参数之一 @PartMap 以map形式传入的multipart参数表现 @Path 用于动态替换URL路径中的path_holder @Query 静态添加请求头(添加固定的请求头) @QueryName 类比@Query,相当于没有value,只有key @QueryMap 以map传入的GET方法的query参数 @Url 用来动态指定一个Url地址 估计参数注解里面最难理解的就是@Url注解了,@Url设置的参数最终会和base url按照一定的规则生成新的url,有下面几种情况。
Retrofit.Builder() 设置的 base url @Url 最终的url 解释 http://192.168.5.14:3033/v1/ books http://192.168.5.14:3033/v1/books @url使用的是相对路径,直接连接在base url的scheme host port后面 http://192.168.5.14:3033/v1/ /v2/books http://192.168.5.14:3033/v2/books @url使用的是绝对路径,接连接在base url http://192.168.5.14:3033/v1/ http://192.168.5.14:3033/books http://192.168.5.14:3033/books @url是一个完整的路径,直接替换base url @url使用的时候还有几点要注意:1. @Url只能设置一次、2. @Url @Path不能同时使用、3. @Url必须在@Query的前面、4. 使用@Url 就不能在方法注解里面@GET,@POST等里面再写路径。
3.2,Retrofit Converter
Retrofit提供的数据转换器,主要转换三种数据,一个是RequestBody数据转换(@Part、@PartMap、@Body里面设置的参数)、一个是ResponseBody数据的转换(服务器的应答数据转换成想要的类型)、一个是对象转换为String(@Path、@Query、@QueryName、@QueryMap、@Header、@HeaderMap、@Field、@FieldMap参数设置的对象转换为String)。和数据转换密切相关的就两个类Converter
/**
* 转换必须实现的方法
*/
T convert(F value) throws IOException;
/** Creates {@link Converter} instances based on a type and target usage. */
abstract class Factory {
/**
* ResponseBody转换成需要的对象,生成Converter<ResponseBody, ?> -> convert 把ResponseBody转换成需要的对象
*/
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit);
/**
* 对象装换成RequestBody,生成Converter<?, RequestBody> -> convert 得到RequestBody
*/
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit);
/**
* 对象装换成String,生成Converter<?, String> -> convert 得到String
*/
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit);
}
Retrofit github上已经默认给我们提供了七八种转换了,用的最多的应该就是GsonConverterFactory和ScalarsConverterFactory,一个是转换成JSON一个是转换成字符串,对应的gradle如下。
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
当我们要自定义Retrofit Converter的时候可以仿照Retrofit提供的几种Converter来实现。
3.3,Retrofit CallAdapter
Retrofit底层的实现是OkHttp,OkHttp网络的执行都是通过OkHttp里面的Call来发起的,Retrofit 里面的CallAdapter就是对OkHttp的Call家的一个适配器。通过这种方式咱们上层调用就不用关心内部的实现,完全解耦。密切相关的类就三个CallAdapter.Factory、CallAdapter、Call。实现CallAdapter.Factory里面的get()方法得到CallAdapter对象,CallAdapter对象里面的adapt()函数又把Call适配成了,我们自定义的对象。在自定义的对象里面会调用到Call里面的一些方法(网络访问相关的一些方法)。
CallAdapter.Factory类里面的一些方法。
/**
* 返回一个CallAdapter对象, 把Call适配成自定义对象,在自定义对象里面调用Call里面的方法。
*/
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
/**
* 举个例子 当Retrofit的注解是这样些时候
* @GET("books/{id}")
* Observable<BookBean> getBookByPath(@Path("id") int id);
* index = 0 的时候 返回的就是BookBean
*/
protected static Type getParameterUpperBound(int index, ParameterizedType type);
/**
*
* 举个例子 当Retrofit的注解是这样些时候
* @GET("books/{id}")
* Observable<BookBean> getBookByPath(@Path("id") int id);
* 返回的就是Observable 的class
*/
protected static Class<?> getRawType(Type type);
CallAdapter就两个方法
/**
* response最终要得到的类型,举个例子
* @GET("books/{id}")
* Observable<BookBean> getBookByPath(@Path("id") int id);
* BookBean就是最终要得到的类型
*/
Type responseType();
/**
* Call适配成自定义的对象
*/
T adapt(Call<R> call);
总而言之不管怎么适配,最终都是要用过Call来调用网络请求,自定义的适配对象也是会拿到到Call对象做一些列的动作。Retrofit最常用的应该就是RxJavaCallAdapterFactory RxJava的适配和ExecutorCallAdapterFactory默认自带的适配。当要自定义自己的适配的时候可以参考这两个适配的实现过程。
//RxJava2.0的适配gradle
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
3.4,Retrofit 的使用
上面很简单的介绍了Retrofit的注解,转换器,适配器。接下里就是怎么使用了。Retrofit使用起来就要比OkHttp简单多了,不用自己去设置RequestBody通过注解就会自动生成。最简单的一次使用如下:
注解接口编写
@GET("books/{id}")
Call<BookBean> getBookByPath(@Path("id") int id);
Retrofit全局配置
Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://192.168.5.14:3033/v1/")
.build();
mNetworkApi = retrofit.create(AppRetrofitApi.class);
网络请求
Call<BookBean> requestCall = mNetworkApi.getBookByPath(0);
requestCall.enqueue(new Callback<BookBean>() {
@Override
public void onResponse(@NonNull Call<BookBean> call, @NonNull Response<BookBean> response) {
BookBean body = response.body();
if (body != null) {
//TODO:body.toString()
}
}
@Override
public void onFailure(@NonNull Call<BookBean> call, Throwable t) {
}
});
四,RxJava
RxJava总结起来就是异步,简洁。RxJava提供了各种各样的操作符,合适的时机使用合适的操作符会是代码逻辑非常的简单。因为RxJava里面的操作符特别的多,接下来就挑几个OkHttp+Retrofit+RxJava二次封装的时候使用到的一些操作符。
- onErrorResumeNext:让原Observable在遇到错误时开始发送第二个Observable的数据序列,我们会用这个操作符做统一的错误处理。原Observable所有的错误都会进入到onErrorResumeNext操作符里面将各种各样的错误转换成自定义的错误。
- compose:对 Observable 整体的变换,将源Observable按照自定义的方式转化成另外一个新的Observable,利用传入的 Transformer 对象的 call 方法直接对自身进行处理。可以在这个操作符里面做网络是否连接的判断,当网络未连接的时候直接Observable.error()结束这个流程。或者配合Rxlifecycle来使用。
- subscribeOn:线程变换操作符,指定的是它之前的操作所在的线程(由OnSubscribe指定线程)。
- unsubscribeOn:有些 Observable 会依赖一些资源,当该 Observable 完成后释放这些资源。如果释放资源比较耗时的话,可以通过 unsubscribeOn 来指定 释放资源代码执行的线程。
- observeOn:线程变换操作符,指定的是它之后的操作所在的线程(由Subscriber指定线程)。
- zipWith:把两个Observable发射的数据组合在一起,我们要自己提供Func2告诉两个Observable结果怎么组合成新的结果。实际使用的时候当两个网络请求要都完成的时候我们才能做下一步的动作可以用zipWith操作符。
- flatMap:将一个发射数据的Observable变换为多个Observable,然后将它们发射的数据合并后放进一个单独的Observable。我们可以用flatMap来做顺序操作。实际使用的时候当前后两个网络请求有关联的时候,我们可以用flatMap操作符来实现。
五,OkHttp+Retrofit+RxJava
前面介绍了一堆,都是为了更好的对OkHttp+Retrofit+RxJava做二次封装。期间也参考了很多网上其他的写法,在别人的写法之上,二次封装实现的目标:
- 每个网络请求都可以单独的设置超时(OkHttpClient.newBuilder()的使用)。
- 每个网络请求都可以单独的设置Converter.Factory。
- 每个网络请求都可以设置访问等级,wifi访问,数据访问(RxJava compose操作符的使用)。
- 支持多个网络请求同时完成的情况下做下一步动作的需求(两个请求可以直接使用ProtocolsClient里面protocolsRequestZipWith()函数实现,超过两个请求就要自己使用RxJava zipWith操作符写链式结构了)。
- 支持多个网络请求相关联的需求,下一个请求需要上一个请求结果的需求(两个请求可以直接使用ProtocolsClient里面protocolsRequestFlatMap()函数实现,超过两个请求就要自己使用RxJava flatMap操作符写链式结构了)。
使用起来也相对的简单:1. 每次请求还得像Retrofit一样写个注解的接口(注解的使用)、2. 继承ProtocolsBaseRequest来实现一些不能在注解里面实现的参数(time out 等等)、3. ProtocolsClient里面调用相应的方法(ProtocolsClient是个单利,是请求的入口)。
5.1,单个网络请求的实现
/**
* 网络请求
*
* @param context context
* @param request request
* @param callback callback
* @param lifecycleTransformer transformer
* @param <T> 类型
*/
public <T> void protocolsRequest(Context context,
ProtocolsBaseRequest<T> request,
ProtocolsBaseCallback<T> callback,
LifecycleTransformer<T> lifecycleTransformer) {
getProtocolsRequestObservable(context, request, lifecycleTransformer).subscribe(
new ProtocolsSubscriber<>(context, request, callback));
}
5.2, 两个网络同时执行完在返回结果的实现(ProtocolsZipFunc告诉两个请求的结果怎么合并成一个新的结果)
/**
* 合并两个请求的结果
*
* @param context context
* @param requestOne request one
* @param requestTwo request two
* @param func 合并我们要得到的结果类型
* @param callback callback
* @param <P> request one 类型
* @param <N> request two 类型
* @param <R> 最终callback返回类型
*/
public <P, N, R> void protocolsRequestZipWith(Context context,
ProtocolsBaseRequest<P> requestOne,
ProtocolsBaseRequest<N> requestTwo,
final ProtocolsZipFunc<P, N, R> func,
ProtocolsBaseCallback<R> callback) {
Func1<Throwable, Observable<R>> mErrorResume = new Func1<Throwable, Observable<R>>() {
@Override
public Observable<R> call(Throwable throwable) {
return Observable.error(transformerException(throwable));
}
};
Observable<P> observableOne = getProtocolsRequestObservable(context, requestOne);
Observable<N> observableTwo = getProtocolsRequestObservable(context, requestTwo);
Observable<R> observable = observableOne.zipWith(observableTwo, new Func2<P, N, R>() {
@Override
public R call(P p, N n) {
return func.call(p, n);
}
})
// .retryWhen(new RetryNetworkException())
.onErrorResumeNext(mErrorResume)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(new ProtocolsSubscriber<>(context, null, callback));
}
5.3, 两个网络请求相互依赖的实现(ProtocolsFlatMapFunc告诉依赖的关系)
/**
* 顺序执行两个请求,并且前后有依赖
*
* @param context context
* @param requestOne request one
* @param requestTwo request two
* @param func 上下来两个请求依赖处理
* @param callback callback
* @param <P> request one 类型
* @param <N> request two 类型
*/
public <P, N> void protocolsRequestFlatMap(final Context context,
final ProtocolsBaseRequest<P> requestOne,
final ProtocolsBaseRequest<N> requestTwo,
final ProtocolsFlatMapFunc<P, N, ProtocolsBaseRequest<N>> func,
ProtocolsBaseCallback<N> callback) {
final Observable<P> observableOne = getProtocolsRequestObservable(context, requestOne);
Func1<Throwable, Observable<N>> mErrorResume = new Func1<Throwable, Observable<N>>() {
@Override
public Observable<N> call(Throwable throwable) {
return Observable.error(transformerException(throwable));
}
};
Observable<N> observable = observableOne.flatMap(new Func1<P, Observable<N>>() {
@Override
public Observable<N> call(P t) {
return getProtocolsRequestObservable(context, func.call(t, requestTwo));
}
})
// .retryWhen(new RetryNetworkException())
.onErrorResumeNext(mErrorResume)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(new ProtocolsSubscriber<>(context, null, callback));
}
5.4, 多个网络请求的实现,就要自己去写链式结构了,参考protocolsRequestFlatMap和protocolsRequestZipWith函数的实现(RxJava flatMap、zipWith操作符的使用)
上面只是简单的解释,更加详细的实现过程,都在源码 https://github.com/tuacy/NetworkFrame
代码里面也有很多需要进一步完善的地方,后面有时间会慢慢的完善上来。