前言 当今网络框架什么最火,当属Okhttp +rxjava +retrofit 三大开源框架结合,简单易用、功能强大。Retrofit 负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp 负责请求的过程,RxJava 负责异步,各种线程之间的切换,三者缺一不可。
简介 本文章使用框架版本为:Okhttp3 +rxjava2 +retrofit2
OkHttp3 HTTP是现代应用程序网络的一种方式。这是我们交换数据和媒体的方式。高效地使用HTTP可以让你的东西加载更快,节省带宽。 OkHttp是一个HTTP客户端,是有效的默认: HTTP/2支持允许所有请求到同一主机共享一个套接字。 连接池减少了请求延迟(如果HTTP/2不可用)。 透明GZIP缩小下载大小。 响应缓存完全避免了网络中的重复请求。 当网络出现问题时,OkHttp会持续运行:它会从常见的连接问题中安静地恢复。如果您的服务有多个IP地址,如果第一次连接失败,OkHttp将尝试替代地址。这对于IPv4+IPv6和驻留在冗余数据中心中的服务是必要的。OkHttp支持现代的TLS特性(TLS 1.3、ALPN、证书固定)。它可以配置为向后扩展连接。 使用OkHttp很容易。它的请求/响应API使用流畅构建器和不变性设计。它支持同步阻塞调用和带有回调的异步调用。
注:OkHttp 支持 Android 2.3 及以上版本Android平台, 对于 Java, JDK 1.7及以上.
Github传送门 官网传送门
Retrofit2 适用于Android和Java的类型安全HTTP客户端。,Retrofit简化了网络请求流程,基于OkHtttp做了封装, 解耦的更彻底:比方说通过注解来配置请求参数,通过工厂来生成CallAdapter,Converter,你可以使用不同的请求适配器(CallAdapter), 比方说RxJava,Java8, Guava。你可以使用不同的反序列化工具(Converter),比方说json, protobuff, xml, moshi等等。
Github传送门 官网传送门
Rxjava2 一个词:异步。
RxJava 在 GitHub 主页上的自我介绍是 “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。其实, RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。它提供一套异步编程的 API,这套 API 是基于观察者模式的,而且是链式调用的,所以使用 RxJava 编写的代码的逻辑会非常简洁。
Github传送门 官网传送门
使用 网上封装的框架库很多,想要正在理解Okhttp3 +rxjava2 +retrofit2组合使用方式,还得自己动手啊!下面会详解搭建的过程。
1. 添加三剑客专属依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 api "com.squareup.okhttp3:okhttp:4.0.0" api "com.squareup.okhttp3:logging-interceptor:4.0.0" api "com.squareup.retrofit2:retrofit:2.2.2" api "com.squareup.retrofit2:converter-gson:2.2.2" api "com.squareup.retrofit2:converter-scalars:2.2.2" api "com.squareup.retrofit2:adapter-rxjava2:2.2.2" api "io.reactivex.rxjava2:rxjava:2.2.18" api "io.reactivex.rxjava2:rxandroid:2.1.1"
如果依赖下载过慢建议使用阿里云镜像,在项目的build.gradle 中添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 repositories { google() maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } } allprojects { repositories { google() maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } maven { url "https://jitpack.io" } } }
2. 封装框架支持的功能 定义一个框架配置类NetworkConfig,其中包含框架支持的功能及初始化的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class NetworkConfig { private String releaseBaseUrl; private String debugBaseUrl; private Application application; private HttpLoggingInterceptor.Level logLevel; private Boolean isOpenCache; private Boolean isOpenRxCache; private List<NetworkHeaderParams> networkHeaderParams; private OkHttpClient.Builder okHttpClientBuild; private ResponseErrorListener responseErrorListener; ...此处省略set/get方法 }
3. 封装获取okhttp及retrofit类 新建Network类代码如下,getOkHttpClientInstance、getRetrofitInstance两个方法为核心方法,用于构造OkHttpClient、Retrofit,该类初始化构造方法入参框架配置类NetworkConfig。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class Network { private final String TAG = Network.class .getSimpleName () ; private static final int CACHE_MAX_SIZE = 10 * 1024 * 1024 ; private final NetworkConfig networkConfig; protected Network (NetworkConfig networkConfig) { this .networkConfig = networkConfig; } private OkHttpClient getOkHttpClientInstance () { HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(message -> Log.e(TAG, message)); httpLoggingInterceptor.level(networkConfig.getLogLevel()); OkHttpClient.Builder builder; if (networkConfig.getOkHttpClientBuild() != null ) { builder = networkConfig.getOkHttpClientBuild(); } else { builder = new OkHttpClient.Builder() .connectTimeout(10 , TimeUnit.SECONDS) .writeTimeout(10 , TimeUnit.SECONDS) .readTimeout(30 , TimeUnit.SECONDS); } if (networkConfig.getOpenCache()) { File httpCacheDirectory = new File(networkConfig.getApplication().getCacheDir(), "common_net_cache" ); try { Cache cache = new Cache(httpCacheDirectory, CACHE_MAX_SIZE); builder.cache(cache); } catch (Exception e) { Log.e(TAG, "Could not create http cache" , e); } builder.addInterceptor(new NetworkCacheInterceptor(networkConfig.getApplication())); } return builder .addInterceptor(new NetworkInterceptor(networkConfig.getNetworkHeaderParams())) .addInterceptor(httpLoggingInterceptor) .addNetworkInterceptor(httpLoggingInterceptor) .build(); } protected Retrofit getRetrofitInstance () { return new Retrofit.Builder() .baseUrl(BuildConfig.DEBUG ? networkConfig.getDebugBaseUrl() : networkConfig.getReleaseBaseUrl()) .client(getOkHttpClientInstance()) .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } }
4. 定义框架内部拦截器 构建NetworkInterceptor拦截器,内部处理外部自定义的NetworkHeaderParams接口配置的信息,可外部会定义多个实现NetworkHeaderParams接口的配置 所以构造函数入参为List networkHeaderParamsList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public interface NetworkHeaderParams { String paramsName () ; String headerName () ; String headerValue () ; } public class NetworkInterceptor implements okhttp3 .Interceptor { private static final String TAG = NetworkInterceptor.class .getSimpleName () ; private final List<NetworkHeaderParams> networkHeaderParamsList; protected NetworkInterceptor (List<NetworkHeaderParams> networkHeaderParamsList) { this .networkHeaderParamsList = networkHeaderParamsList; } @Override public Response intercept (Chain chain) throws IOException { Request request = chain.request(); Request.Builder requestBuilder = request.newBuilder(); if (networkHeaderParamsList != null && networkHeaderParamsList.size() > 0 ) { for (NetworkHeaderParams networkHeaderParams : networkHeaderParamsList) { addHeaderParams(request, requestBuilder, networkHeaderParams); } } return chain.proceed(requestBuilder.build()); } private void addHeaderParams (Request request, Request.Builder requestBuilder, NetworkHeaderParams networkHeaderParams) { String paramStr = request.header(networkHeaderParams.paramsName()); boolean isAddHeader = false ; if (!TextUtils.isEmpty(paramStr) && NetworkLoader.DEFAULT_IS_ADD_HEADER.equals(paramStr)) { isAddHeader = true ; } if (isAddHeader) { requestBuilder .addHeader(networkHeaderParams.headerName(), networkHeaderParams.headerValue()) .removeHeader(networkHeaderParams.paramsName()); } else { requestBuilder .removeHeader(networkHeaderParams.paramsName()); } } }
构建NetworkCacheInterceptor拦截器,用于缓存网络请求值,定义了缓存时长及读取缓存时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class NetworkCacheInterceptor implements Interceptor { private Context context; protected NetworkCacheInterceptor (Context context) { this .context = context; } @Override public Response intercept (Chain chain) throws IOException { Request request = chain.request(); if (NetworkUtil.isNetworkAvailable(context)) { Response response = chain.proceed(request); int maxAge = 60 ; return response.newBuilder() .removeHeader("Pragma" ) .removeHeader("Cache-Control" ) .header("Cache-Control" , "public, max-age=" + maxAge) .build(); } else { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); Response response = chain.proceed(request); int maxStale = 60 * 60 * 24 * 3 ; return response.newBuilder() .removeHeader("Pragma" ) .removeHeader("Cache-Control" ) .header("Cache-Control" , "public, only-if-cached, max-stale=" + maxStale) .build(); } } }
5. 封装网络初始化配置入口 回顾下上面配置的类,构成函数,或者方法、属性基本都是protected、protected,遵循开闭原则,这里需要公开框架提供给外部使用的入口及配置。
使用建造者模式定义初始化网络框架配置,内部设置默认值。外部初始化可不构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class NetworkLoader { public static class Build { private NetworkConfig networkConfig; public Build (Application application) { networkConfig = new NetworkConfig(); networkConfig.setApplication(application); networkConfig.setLogLevel(HttpLoggingInterceptor.Level.NONE); networkConfig.setOpenCache(false ); networkConfig.setOpenRxCache(false ); networkConfig.setResponseErrorListener(new BaseResponseErrorListenerImpl()); } public Build setBaseUrl (String releaseBaseUrl, String debugBaseUrl) { networkConfig.setReleaseBaseUrl(releaseBaseUrl); networkConfig.setDebugBaseUrl(debugBaseUrl); return this ; } public Build setLogLevel (HttpLoggingInterceptor.Level logLevel) { networkConfig.setLogLevel(logLevel); return this ; } public Build setOpenCache (boolean isOpenCache) { networkConfig.setOpenCache(isOpenCache); return this ; } public Build setOpenRxCache (boolean isOpenRxCache) { networkConfig.setOpenRxCache(isOpenRxCache); return this ; } public Build setNetworkHeaderParams (NetworkHeaderParams networkHeaderParams) { if (networkConfig.getNetworkHeaderParams() == null ) { networkConfig.setNetworkHeaderParams(new ArrayList<>()); } networkConfig.getNetworkHeaderParams().add(networkHeaderParams); return this ; } public Build setOkHttpClientBuild (OkHttpClient.Builder okHttpClientBuild) { networkConfig.setOkHttpClientBuild(okHttpClientBuild); return this ; } public Build setResponseErrorListener (ResponseErrorListener responseErrorListener) { networkConfig.setResponseErrorListener(responseErrorListener); return this ; } public Build create () { NetworkLoader.getInstance().setNetworkConfig(networkConfig); return this ; } public void build () { NetworkLoader.getInstance().build(); } } }
上方NetworkLoader类定义框架公开的Build类用于构建框架所需配置,下方代码该类中添加私有的方法build()用于创建Okhttp加retrofit,此处NetworkLoader类定义为全局单例的类,使用容器的方式定义单例模式。公开了框架初始化的Retrofit、RxCache、RxError对象方便外部使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 public class NetworkLoader { public static final String TAG = NetworkLoader.class .getSimpleName () ; public static final String DEFAULT_IS_ADD_HEADER = "true" ; private final static Map<String, NetworkLoader> networkLoaderMaps = new HashMap<>(); private NetworkConfig networkConfig = null ; private Retrofit retrofit = null ; private RxCache rxCache = null ; private RxErrorHandler rxErrorHandler = null ; private void setNetworkConfig (NetworkConfig networkConfig) { this .networkConfig = networkConfig; } public static NetworkLoader getInstance () { if (!networkLoaderMaps.containsKey(TAG)) { networkLoaderMaps.put(TAG, new NetworkLoader()); } return networkLoaderMaps.get(TAG); } private void build () { if (networkConfig == null ) { Log.e(TAG, "未设置网络框架配置,清使用NetworkLoader.Build类创建" ); } if (TextUtils.isEmpty(networkConfig.getDebugBaseUrl()) || TextUtils.isEmpty(networkConfig.getReleaseBaseUrl())) { Log.e(TAG, "未设置网络框架基础请求地址,请配置debug或者release请求地址" ); } setRetrofit(new Network(networkConfig).getRetrofitInstance()); if (networkConfig.getOpenRxCache()) { File rxCacheDirectory = new File(networkConfig.getApplication().getCacheDir(), "common_rx_cache" ); if (!rxCacheDirectory.exists()) { rxCacheDirectory.mkdirs(); } setRxCache(new RxCache.Builder() .persistence(rxCacheDirectory, new GsonSpeaker())); } setRxErrorHandler(RxErrorHandler .builder() .with(networkConfig.getApplication()) .responseErrorListener(networkConfig.getResponseErrorListener()).build()); } public RxCache getRxCache () { return rxCache; } private void setRxCache (RxCache rxCache) { this .rxCache = rxCache; } private void setRetrofit (Retrofit retrofit) { this .retrofit = retrofit; } public RxErrorHandler getRxErrorHandler () { return rxErrorHandler; } private void setRxErrorHandler (RxErrorHandler rxErrorHandler) { this .rxErrorHandler = rxErrorHandler; } @NonNull public Retrofit getRetrofit () { return retrofit; } public static class Build { private NetworkConfig networkConfig; public Build (Application application) { networkConfig = new NetworkConfig(); networkConfig.setApplication(application); networkConfig.setLogLevel(HttpLoggingInterceptor.Level.NONE); networkConfig.setOpenCache(false ); networkConfig.setOpenRxCache(false ); networkConfig.setResponseErrorListener(new BaseResponseErrorListenerImpl()); } public Build setBaseUrl (String releaseBaseUrl, String debugBaseUrl) { networkConfig.setReleaseBaseUrl(releaseBaseUrl); networkConfig.setDebugBaseUrl(debugBaseUrl); return this ; } public Build setLogLevel (HttpLoggingInterceptor.Level logLevel) { networkConfig.setLogLevel(logLevel); return this ; } public Build setOpenCache (boolean isOpenCache) { networkConfig.setOpenCache(isOpenCache); return this ; } public Build setOpenRxCache (boolean isOpenRxCache) { networkConfig.setOpenRxCache(isOpenRxCache); return this ; } public Build setNetworkHeaderParams (NetworkHeaderParams networkHeaderParams) { if (networkConfig.getNetworkHeaderParams() == null ) { networkConfig.setNetworkHeaderParams(new ArrayList<>()); } networkConfig.getNetworkHeaderParams().add(networkHeaderParams); return this ; } public Build setOkHttpClientBuild (OkHttpClient.Builder okHttpClientBuild) { networkConfig.setOkHttpClientBuild(okHttpClientBuild); return this ; } public Build setResponseErrorListener (ResponseErrorListener responseErrorListener) { networkConfig.setResponseErrorListener(responseErrorListener); return this ; } public Build create () { NetworkLoader.getInstance().setNetworkConfig(networkConfig); return this ; } public void build () { NetworkLoader.getInstance().build(); } } }
到此手动搭建轻量级的Okhttp3 +rxjava2 +retrofit2网络框架搭建完毕,根据自身项目使用动态自己调整。自己动手搭建的才是最熟悉使用最方便的。希望看完这篇文章小伙伴都能搭建出属于自己风格的网络框架哦。
APP中实战使用 1.在Application中初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MyApplication extends Application { @Override public void onCreate () { super .onCreate(); new NetworkLoader.Build(this ) .setBaseUrl("项目正式地址" ,"项目测试地址" ) .setLogLevel(HttpLoggingInterceptor.Level.HEADERS) .setOpenCache(true ) .setOpenRxCache(true ) .setNetworkHeaderParams(new TokenParams()) .setResponseErrorListener(new CustomResponseErrorListenerImpl()) .create().build(); } }
2.创建使用类 定义TestController类添加Retrofit接口、定义TestService类添加请求对应Rxjava返回值接口、定义TestServiceImpl接口实现类继承框架自带Base类,内部处理Reftofit创建请求方法通过 getApi().getTop()获取TestController配置的接口方法返回值为Rxjava中Observable类型,外部使用通过TestServiceImpl单例模式执行getTop方法获取接口响应结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public interface TestController { @Headers ({ "token:true" }) @GET ("data/sk/101010100.html" ) Observable<BaseApiResult<String>> getTop(); } public interface TestService { Observable<BaseApiResult<String>> getTop(); } public class TestServiceImpl extends BaseServiceImpl <TestController > implements TestService { private static TestServiceImpl instance = null ; private TestServiceImpl () { super (TestController.class ) ; } public static TestServiceImpl getInstance () { if (instance == null ) { synchronized (TestServiceImpl.class ) { if (instance == null ) { instance = new TestServiceImpl(); } } } return instance; } @Override public Observable<BaseApiResult<String>> getTop() { return getApi().getTop().compose(RxBus.ApplySchedulers()); } } TestServiceImpl.getInstance().getTop().subscribe(data -> { Log.e("MainActivity" , "subscribe: " +JSON.toJSONString(data)); }, throwable -> { Log.e("MainActivity" , "onCreate: " , throwable); }); public class BaseServiceImpl <Controller > { private Controller controller; public BaseServiceImpl (Class<Controller> clazz) { try { controller = NetworkLoader.getInstance().getRetrofit().create(clazz); }catch (Exception e){ Log.e(NetworkLoader.TAG, "RetrofitError: 配置网络框架时未执行build初始化,请检查" ); } } protected Controller getApi () { return controller; } }
总结 一切的学习都是需要实践的,没有看过只有写过。希望大家都能去自己动手写一写。此网络框架后期会不断添加和网络请求相关的功能及组件。本文没有介绍目前支持的一些高级功能的使用,下篇文章会介绍使用RxCahce、Rxerrorhandler、Rxpermissions及自定义请求Header头接口这些相对高级的功能。
框架及Demo源码 源码地址 MiQingWang/CommonNetFrame
注:框架额外配置依赖后续章节介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 api "com.github.VictorAlbertos.RxCache:runtime:1.8.3-2.x" api "com.github.VictorAlbertos.Jolyglot:gson:0.0.4" api "me.jessyan:rxerrorhandler:2.1.1" api "com.github.tbruyelle:rxpermissions:0.10.2" api "com.google.code.gson:gson:2.8.6" api "com.alibaba:fastjson:1.2.68"