🔥 介绍
本文通过OkHttp+Retrofit+Rxjava+Hilt实现一个网络请求框。
💥 最终代码
iWanAndroidService.register(map) .compose(ResponseTransformer.obtain()) .subscribe(registerData -> { //请求成功 }, new ErrorConsumer() { @Override protected void error(ApiException e) { //请求失败 } });
是不是特别省事。
💥 项目结构
🔥 OkHttp
💥 OkHttp是什么
OkHttp 是一个默认高效的 HTTP 客户端:
- HTTP/2 支持允许对同一主机的所有请求共享一个 socket。
- 连接池减少了请求延迟(如果 HTTP/2 不可用)。
- 透明 GZIP 可缩小下载大小。
- 响应缓存完全避免网络重复请求。
- 网路出现问题后,OkHttp会保持不变,自动从问题中恢复。
OkHttp的时候就不单独介绍了。
缺陷:
- 网络请求的接口配置繁琐,尤其是需要配置复杂请求body,请求头,参数的时候;
- 数据解析过程需要用户手动拿到responsbody进行解析,不能复用;
- 无法适配自动进行线程的切换。
- 万一我们的存在嵌套网络请求就会陷入“回调陷阱”。
🔥 Retrofit
💥 Retrofit是什么
Retrofit 基于 OkHttp,网络请求工作实际由OkHttp完成,而Retrofit主要负责接口的封装。
Retrofit不仅具备了OkHttp的高效特性,还有以下优势:
- 支持RESTful API设计风格。
- 通过注解配置请求:包括请求方法、请求参数、请求头,返回值等。
- 可以搭配多种Converter将获得的数据自动解析和序列化:支持Gson,Jackson,Protobuff等。提供了对 RxJava 的支持。
- 请求速度快,使用非常方便灵活。
注意:Retrofit不具备网络请求功能,因此你要设置分发器拦截器等则需要在OkHttpClient中设置。
💥 Retrofit注解
官方文档也提供了各类注解的用法。
🔥 OkHttp+Retrofit 实例
💥 添加依赖
dependencies { implementation 'com.squareup.retrofit2:retrofit:2.8.1' // 必要依赖,retrofit implementation 'com.squareup.retrofit2:converter-gson:2.8.1' // 必要依赖,解析json字符 implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' //非必要依赖,打印日志 }
💥 定义请求接口
public interface IWanAndroidService { String BASE_URL = "https://www.wanandroid.com/"; @GET("banner/json") Call<ResponseData<List<HomeBanner>>> homeBanner(); @POST("user/register") @FormUrlEncoded Call<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map); }
💥 设置OkHttp+Retrofit
public class NetworkManager { private static volatile NetworkManager instances; private static volatile OkHttpClient okHttpClient; private static volatile Retrofit retrofit; public static NetworkManager getInstance() { if (instances == null) { synchronized (NetworkManager.class) { if (instances == null) { instances = new NetworkManager(); } } } return instances; } private static int TIME_OUT = 30; //30秒超时断开连接 private OkHttpClient initClient() { if (okHttpClient == null) { synchronized (NetworkManager.class) { if (okHttpClient == null) { //请求日志打印 HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> { try { MLog.e(URLDecoder.decode(message, "utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); MLog.e(message); } }); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); //注释1:创建OkHttpClient okHttpClient = new OkHttpClient.Builder() .sslSocketFactory(new NetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert) .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .addInterceptor(loggingInterceptor) .readTimeout(TIME_OUT, TimeUnit.SECONDS) .writeTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); } } } return okHttpClient; } public Retrofit initRetrofit() { if (retrofit == null) { synchronized (NetworkManager.class) { if (retrofit == null) { //注释2:创建Retrofit retrofit = new Retrofit.Builder() .client(initClient())//选填 .baseUrl(IWanAndroidService.BASE_URL)//必填 .addConverterFactory(GsonConverterFactory.create())//选填(数据转换器,解析) .build(); } } } return retrofit; } }
- 注释1:创建OkHttpClient对象,构建一个网络类型的实例,一般会将所有的网络请求使用同一个单例对象。(如果OkHttpClient使用默认,可不设置)
- 注释2:创建Retrofit对象,构建一个网络请求的载体对象,在build的时候有非常多的初始化内容,如设置OkHttpClient、设置请求的url,添加数据转换器等。
💥 网络请求
//GET //注释1:动态获取IWanAndroidService对象 IWanAndroidService service = NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class); //注释2:网络请求 service.homeBanner().enqueue(new Callback<ResponseData<List<HomeBanner>>>() { @Override public void onResponse(Call<ResponseData<List<HomeBanner>>> call, Response<ResponseData<List<HomeBanner>>> response) { if (response.body().getData() != null) { MLog.e(response.body().getData().get(0).toString()); binding.loginTvContent.setText(response.body().getData().get(0).toString()); } } @Override public void onFailure(Call<ResponseData<List<HomeBanner>>> call, Throwable t) { MLog.e(t.getMessage()); } }); //POST Map<String, String> map = new HashMap<>(); map.put("username", account); map.put("password", passsword); map.put("repassword", passsword); NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class) .register(map).enqueue(new Callback<ResponseData<RegisterData>>() { @Override public void onResponse(Call<ResponseData<RegisterData>> call, Response<ResponseData<RegisterData>> response) { if (response.body().getData() != null) { MLog.e(response.body().getData().toString()); binding.loginTvContent.setText(response.body().getData().toString()); } } @Override public void onFailure(Call<ResponseData<RegisterData>> call, Throwable t) { MLog.e(t.getMessage()); } });
Retrofit的精髓:为统一配置网络请求完成动态代理的设置。
💥 效果图
🔥 Rxjava
RxJava使用了观察者模式和建造者模式中的链式调用。
观察者模式:
Observable(被观察者)被Observer(观察者)订阅(Subscribe)之后,Observable在发出消息的时候会通知对应的Observer,并且,一个Observable可以有被多个Observer订阅。
链式调用:调用对应的方法对原对象进行处理后返回原对象,从而做到链式调用。
参与者:
- Observable:被观察者,也就是消息的发送者
- Observer:观察者,消息的接收者
- Subscriber:订阅者,观察者的另一种表示
- Scheduler:调度器,进行线程切换
RxJava当然是优秀而且强大的,有以下优势:
- 具备响应式编程该有的特性。
- 为异步而生,无需手动创建线程,并具备线程切换能力。
- 支持链式调用,保证代码的简洁性。
- 各种操作符,功能非常强大,满足各种业务需求。
- 简化了异常的处理。
RxJava适用场景:网络请求、数据库读写、文件读写、定时任务等各种耗时操作需要通过异步来完成的操作都可以使用RxJava。
💥 添加依赖(新增)
implementation "io.reactivex.rxjava2:rxjava:2.2.6" // 必要rxjava依赖 implementation "io.reactivex.rxjava2:rxandroid:2.1.0" // 必要rxandrroid依赖,切线程时需要用到 ... implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' // 必要依赖,和rxjava结合必须用到
💥 修改请求接口
public interface IWanAndroidService { String BASE_URL = "https://www.wanandroid.com/"; //OkHttp+Retrofit //OkHttp+Retrofit+RxJava @GET("banner/json") Observable<ResponseData<List<HomeBanner>>> homeBanner(); @POST("user/register") @FormUrlEncoded Observable<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map); }
💥 设置OkHttp+Retrofit+RxJava
1. public Retrofit initRetrofitRxJava() { if (retrofit == null) { synchronized (NetworkManager.class) { if (retrofit == null) { retrofit = new Retrofit.Builder() .client(initClient())//选填 .baseUrl(IWanAndroidService.BASE_URL)//必填 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//新增网络请求适配器 .addConverterFactory(GsonConverterFactory.create())//选填(数据转换器,解析) .build(); } } } return retrofit; }
💥 进行网络请求
NetworkManager.getInstance().initRetrofitRxJava() .create(IWanAndroidService.class) .homeBanner() .subscribeOn(Schedulers.io())//切换到IO线程 .observeOn(AndroidSchedulers.mainThread())//切换到主线程 // 添加订阅 .subscribe(listResponseData -> { //请求成功 if (listResponseData != null) { MLog.e(listResponseData.getData().get(0).toString()); binding.loginTvContent.setText(listResponseData.getData().get(0).toString()); } }, throwable -> { //请求失败 MLog.e(throwable.getMessage()); });
💥 效果图
💥 进一步封装
由于请求过于繁琐,咱们试着复进一步封装。
🌀 统一异常处理(自定义ApiException)
public class ApiException extends Exception { //未知错误 public static final int UNKNOWN = 1000; //解析错误 public static final int PARSE_ERROR = 1001; //网络错误/连接错误 public static final int NETWORK_ERROR = 1002; private int code; private String displayMessage; public ApiException(int code, String displayMessage) { this.code = code; this.displayMessage = displayMessage; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getDisplayMessage() { return displayMessage; } public void setDisplayMessage(String displayMessage) { this.displayMessage = displayMessage; } public static ApiException handleException(Throwable e) { ApiException ex; if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) { //解析错误 ex = new ApiException(PARSE_ERROR, e.getMessage()); return ex; } else if (e instanceof ConnectException) { //网络错误 ex = new ApiException(NETWORK_ERROR, e.getMessage()); return ex; } else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) { //连接错误 ex = new ApiException(NETWORK_ERROR, e.getMessage()); return ex; } else { //未知错误 ex = new ApiException(UNKNOWN, e.getMessage()); return ex; } } }
🌀 统一异常处理(实现Consumer<Throwable>
接口)
public abstract class ErrorConsumer implements Consumer<Throwable> { @Override public void accept(@NotNull Throwable throwable) throws Exception { //对异常进行处理 ApiException exception; if (throwable instanceof ApiException) { exception = (ApiException) throwable; } else { exception = ApiException.handleException(throwable); } //调用error方法 error(exception); } //使用时实现error方法即可。 protected abstract void error(ApiException e); }
🌀 响应转换处理
public class ResponseTransformer<T> implements ObservableTransformer<ResponseData<T>, T> { public ResponseTransformer() { } public static <R> ResponseTransformer<R> obtain(){ return new ResponseTransformer<>(); } @NotNull @Override public ObservableSource<T> apply(@NotNull Observable<ResponseData<T>> upstream) { return upstream.onErrorResumeNext(new Function<Throwable, ObservableSource<? extends ResponseData<T>>>() { @Override public ObservableSource<? extends ResponseData<T>> apply(@NotNull Throwable throwable) throws Exception { return Observable.error(ApiException.handleException(throwable)); } }).flatMap(new Function<ResponseData<T>, ObservableSource<T>>() { @Override public ObservableSource<T> apply(@NotNull ResponseData<T> responseData) throws Exception { //请求成功,开始处理你的逻辑吧 if (0==responseData.getErrorCode()) { if (null!=responseData.getData()) { return Observable.just(responseData.getData()); } else { //有可能存在返回的数据结果为ull,直接传Null会产生异常。 //用过反射创建一个没有内容的数据实例。 return Observable.just(responseData.getData()); } } //请求异常 return Observable.error(new ApiException(responseData.getErrorCode(), responseData.getErrorMsg())); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } }
🌀 封装后使用
数据拿到。
🔥 Hilt(Jetpack成员)
在 Android 上使用Hilt进行依赖注入。Hilt 建立在 Dagger 之上,它提供了一种将 Dagger 依赖注入合并到 Android 应用程序中的标准方法。
💥 添加依赖(新增)
implementation 'com.google.dagger:hilt-android:2.40.1' annotationProcessor 'com.google.dagger:hilt-compiler:2.40.1'
💥 Hilt Gradle plugin
🌀 build.gradle(Project)
buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.google.dagger:hilt-android-gradle-plugin:2.40.1' } }
🌀 build.gradle(Module)
apply plugin: 'dagger.hilt.android.plugin'
🌀 Hilt Application
所有使用 Hilt 的应用程序都必须包含一个用 @HiltAndroidApp 注释的 Application 类。
创建Application
import android.app.Application; import dagger.hilt.android.HiltAndroidApp; @HiltAndroidApp public class App extends Application { }
设置AndroidManifest
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.scc.wanandroid"> <uses-permission android:name="android.permission.INTERNET"/> <application android:name=".App" ...> </application> </manifest>
准备工作做完了,开始使用Hilt搭建网络框架
💥 设置OkHttp+Retrofit+RxJava+Hilt
🌀 创建NetworkModule用来初始化
1.@InstallIn(SingletonComponent.class) @Module public class NetworkModule { private static int TIME_OUT = 30; //30秒超时断开连接 @Provides @Singleton OkHttpClient providesOkHttpClient(){ HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { try { MLog.e("--network--", URLDecoder.decode(message, "utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); MLog.e("--network--", message); } } }); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); return new OkHttpClient.Builder() .sslSocketFactory(new NetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert) .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .addInterceptor(loggingInterceptor) .readTimeout(TIME_OUT, TimeUnit.SECONDS) .writeTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); } @Singleton @Provides Retrofit providesRetrofit(OkHttpClient okHttpClient){ return new Retrofit.Builder() .client(okHttpClient) .baseUrl(IWanAndroidService.BASE_URL) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); } @Singleton @Provides IWanAndroidService providesWanAndroidService(Retrofit retrofit){ return retrofit.create(IWanAndroidService.class); } }
🌀 使用
@AndroidEntryPoint public class LoginActivity extends AppCompatActivity { ActivityLoginBinding binding; @Inject IWanAndroidService iWanAndroidService; //Retrofit+RxJava+Hilt iWanAndroidService.homeBanner() .compose(ResponseTransformer.obtain()) .subscribe(homeBanners -> { //请求成功 if (homeBanners != null) { MLog.e(homeBanners.get(0).toString()); binding.loginTvContent.setText(homeBanners.get(0).toString()); } }, new ErrorConsumer() { @Override protected void error(ApiException e) { //请求失败 MLog.e(e.getMessage()+e.getCode()); } }); }
🌀 效果图
🔥 小结
项目拿到就能用,当然还有可优化的地方。
- 获取的data为null的时候的处理。
- 对外隐藏进行网络请求的实现细节。
- 根据实际情况进一步细化,如API分模块创建等。