Android OkHttp+Retrofit+Rxjava+Hilt实现网络请求框架

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 🔥 介绍本文通过OkHttp+Retrofit+Rxjava+Hilt实现一个网络请求框。

🔥 介绍


本文通过OkHttp+Retrofit+Rxjava+Hilt实现一个网络请求框。


💥 最终代码


        iWanAndroidService.register(map)
                .compose(ResponseTransformer.obtain())
                .subscribe(registerData -> {
                    //请求成功
                }, new ErrorConsumer() {
                    @Override
                    protected void error(ApiException e) {
                        //请求失败
                    }
                });


是不是特别省事。


💥 项目结构


微信图片_20220524143416.png


🔥 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注解


微信图片_20220524143729.png


官方文档也提供了各类注解的用法。


🔥 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的精髓:为统一配置网络请求完成动态代理的设置。


💥 效果图


微信图片_20220524143949.gif


🔥 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结合必须用到


💥 修改请求接口


微信图片_20220524144143.png


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());
                });


💥 效果图


微信图片_20220524144302.png


💥 进一步封装


由于请求过于繁琐,咱们试着复进一步封装。


🌀 统一异常处理(自定义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());
    }
}


🌀 封装后使用


微信图片_20220524144427.png


数据拿到。


🔥 Hilt(Jetpack成员)


在 Android 上使用Hilt进行依赖注入。Hilt 建立在 Dagger 之上,它提供了一种将 Dagger 依赖注入合并到 Android 应用程序中的标准方法。


微信图片_20220524144454.png


官方文档最为致命


💥 添加依赖(新增)


  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());
                    }
                });
}


🌀 效果图


微信图片_20220524144847.png


🔥 小结


项目拿到就能用,当然还有可优化的地方。


  • 获取的data为null的时候的处理。
  • 对外隐藏进行网络请求的实现细节。
  • 根据实际情况进一步细化,如API分模块创建等。


💥 项目传送门


🔥 链接汇总


🌀 OkHttp Github


🌀 Retrofit Github


🌀 RxJava Github


🌀 Hilt 官方文档最为致命


🔥 感谢


🌀 感谢鸿神wanandroid提供的API


🌀 Android Retrofit + RxJava使用详解


🌀 封装Retrofit2+RxJava2网络请求框架

相关文章
|
1月前
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
126 0
|
8天前
|
Java 调度 Android开发
深入解析Android应用开发中的响应式编程与RxJava应用
在现代Android应用开发中,响应式编程及其核心框架RxJava正逐渐成为开发者的首选。本文将深入探讨响应式编程的基本概念、RxJava的核心特性以及如何在Android应用中利用RxJava提升代码的可读性和性能。 【7月更文挑战第7天】
|
20天前
|
缓存 网络协议 安全
Android网络面试题之Http基础和Http1.0的特点
**HTTP基础:GET和POST关键差异在于参数传递方式(GET在URL,POST在请求体),安全性(POST更安全),数据大小限制(POST无限制,GET有限制),速度(GET较快)及用途(GET用于获取,POST用于提交)。面试中常强调POST的安全性、数据量、数据类型支持及速度。HTTP 1.0引入了POST和HEAD方法,支持多种数据格式和缓存,但每个请求需新建TCP连接。**
24 5
|
18天前
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
24 2
|
24天前
|
缓存 JSON 网络协议
Android面试题:App性能优化之电量优化和网络优化
这篇文章讨论了Android应用的电量和网络优化。电量优化涉及Doze和Standby模式,其中应用可能需要通过用户白名单或电池广播来适应限制。Battery Historian和Android Studio的Energy Profile是电量分析工具。建议减少不必要的操作,延迟非关键任务,合并网络请求。网络优化包括HTTPDNS减少DNS解析延迟,Keep-Alive复用连接,HTTP/2实现多路复用,以及使用protobuf和gzip压缩数据。其他策略如使用WebP图像格式,按网络质量提供不同分辨率的图片,以及启用HTTP缓存也是有效手段。
42 9
|
19天前
|
缓存 网络协议 Android开发
Android网络面试题之Http1.1和Http2.0
HTTP/1.1 引入持久连接和管道机制提升效率,支持分块传输编码和更多请求方式如PUT、PATCH。Host字段指定服务器域名,RANGE用于断点续传。HTTP/2变为二进制协议,实现多工处理,头信息压缩和服务器推送,减少延迟并优化资源加载。HTTP不断发展,从早期的简单传输到后来的高效交互。
22 0
Android网络面试题之Http1.1和Http2.0
|
24天前
|
JSON Java API
【Android】使用 Retrofit2 发送异步网络请求的简单案例
**摘要:** Retrofit是Android和Java的HTTP客户端库,简化了RESTful API交互。它通过Java接口定义HTTP请求,并提供注解管理参数、HTTP方法等。要使用Retrofit,首先在AndroidManifest.xml中添加`INTERNET`权限,然后在`build.gradle`中引入Retrofit和Gson依赖。创建服务器响应数据类和描述接口的接口,如`Result`和`Api`。通过Retrofit.Builder配置基础URL并构建实例,之后调用接口方法创建Call对象并发送异步请求。
50 1
|
28天前
|
缓存 网络协议 Java
Android面试题之Java网络通信基础知识
Socket是应用与TCP/IP通信的接口,封装了底层细节。网络通信涉及连接、读写数据。BIO是同步阻塞,NIO支持多路复用(如Selector),AIO在某些平台提供异步非阻塞服务。BIO示例中,服务端用固定线程池处理客户端请求,客户端发起连接并读写数据。NIO的关键是Selector监控多个通道的事件,减少线程消耗。书中推荐《Java网络编程》和《UNIX网络编程》。关注公众号AntDream了解更多。
24 2
|
29天前
|
XML JSON Java
Android面试题 之 网络通信基础面试题
序列化对比:Serializable码流大、性能低;XML人机可读但复杂;JSON轻量、兼容性好但空间消耗大;ProtoBuff高效紧凑。支持大量长连接涉及系统限制调整、缓冲区优化。select/poll/epoll是IO多路复用,epoll在高连接数下性能更优且支持边缘触发。水平触发持续通知数据,边缘触发仅通知新数据。直接内存减少一次拷贝,零拷贝技术如sendfile和MMAP提升效率。关注公众号&quot;AntDream&quot;了解更多技术细节。
18 1
|
1月前
|
缓存 Android开发 Kotlin
【安卓app开发】kotlin Jetpack Compose框架 | 先用OKhttp下载远程音频文件再使用ExoPlayer播放
使用 Kotlin 的 Jetpack Compose 开发安卓应用时,可以结合 OkHttp 下载远程音频文件和 ExoPlayer 进行播放。在 `build.gradle` 添加相关依赖后,示例代码展示了如何下载音频并用 ExoPlayer 播放。代码包括添加依赖、下载文件、播放文件及简单的 Compose UI。注意,示例未包含完整错误处理和资源释放,实际应用需补充这些内容。