OkHttp、Retrofit、RxJava:一文讲清楚

news/2025/2/26 19:28:29

一、okHttp的同步和异步请求

Call 是 OkHttp 的核心接口,代表一个已准备好执行的 HTTP 请求。它支持 同步 和 异步 两种模式:

        enqueue——>okHttp异步

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
    .url("https://example.com")
    .build();

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        System.out.println("Response: " + response.body().string());
    }

    @Override
    public void onFailure(Call call, IOException e) {
        System.err.println("Request failed: " + e.getMessage());
    }
});

        execute——>同步请求

        特点

  1. 阻塞线程
    请求发送后,当前线程会阻塞,直到服务器返回响应或超时。

  2. 直接返回结果
    通过 execute() 方法直接返回 Response 对象,无需回调。

  3. 线程管理
    不能在主线程中执行,否则会触发 NetworkOnMainThreadException 异常。同步请求必须在后台线程中执行,可以使用 ThreadExecutorService 或 RxJava 等工具管理线程。

  4. 资源释放
    Response 对象实现了 Closeable 接口,使用 try-with-resources 语法确保资源释放。

  5. 超时设置
    默认超时为 10 秒,可通过 OkHttpClient.Builder 自定义:

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
    .url("https://example.com")
    .build();
try (Response response = client.newCall(request).execute()) {
    if (response.isSuccessful()) {
        System.out.println("Response: " + response.body().string());
    } else {
        System.err.println("Request failed: " + response.code());
    }
} catch (IOException e) {
    System.err.println("Request error: " + e.getMessage());
}

    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.execute(() -> {
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println
    特性同步请求(execute异步请求(enqueue
    线程阻塞阻塞当前线程,直到请求完成不阻塞线程,后台执行
    结果获取直接返回 Response 对象通过 Callback 回调处理结果
    线程管理需手动管理线程,避免主线程阻塞自动在后台线程执行,主线程无影响
    适用场景需立即获取结果的场景(如单元测试)需异步处理的场景(如网络请求)

    二、okHttp+Rxjava

    Observable<Response> observable = Observable.create(emitter -> {
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                if (!emitter.isDisposed()) {
                    emitter.onNext(response);
                    emitter.onComplete();
                }
            }
    
            @Override
            public void onFailure(Call call, IOException e) {
                if (!emitter.isDisposed()) {
                    emitter.onError(e);
                }
            }
        });
        emitter.setCancellable(call::cancel);
    });
    
    observable
        .subscribeOn(Schedulers.io()) // 在 IO 线程执行
        .observeOn(AndroidSchedulers.mainThread()) // 在主线程处理结果
        .subscribe(new Observer<Response>() {
            @Override
            public void onSubscribe(Disposable d) {
                // 订阅时调用
            }
    
            @Override
            public void onNext(Response response) {
                System.out.println("Response: " + response.body().string());
            }
    
            @Override
            public void onError(Throwable e) {
                System.err.println("Error: " + e.getMessage());
            }
    
            @Override
            public void onComplete() {
                // 请求完成时调用
            }
        });

    二、Retrofit 的同步与异步用法,用Call发起请求

    Retrofit 内部使用 OkHttp 作为 HTTP 客户端,这意味着在 Retrofit 的配置中,你可以定制 OkHttp 的各种参数(如超时设置、拦截器等)。

    为了更好地支持异步编程,Retrofit 提供了 RxJava 的 CallAdapter。通过这个适配器,你可以让 API 接口直接返回 Observable、Single 等 RxJava 类型,从而使用 RxJava 的强大操作符来处理请求结果和错误。

    public interface ApiService {
        @GET("users")
        Call<List<User>> getUsers();
    }
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())  // 使用 Gson 进行 JSON 转换
            .build();
    
    ApiService apiService = retrofit.create(ApiService.class);
    

    Retrofit同步请求

            需在子线程中调用,否则会触发 NetworkOnMainThreadException

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Call<List<User>> call = apiService.getUsers();
                Response<List<User>> response = call.execute();
                if (response.isSuccessful() && response.body() != null) {
                    List<User> users = response.body();
                    // 处理返回的数据
                } else {
                    Log.e("Retrofit", "Response error: " + response.code());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
    

    Retrofit异步请求

    Call<List<User>> call = apiService.getUsers();
    call.enqueue(new Callback<List<User>>() {
        @Override
        public void onResponse(Call<List<User>> call, Response<List<User>> response) {
            if (response.isSuccessful() && response.body() != null) {
                List<User> users = response.body();
                // 处理返回的数据,比如更新 UI
            } else {
                // 请求成功,但是服务器返回错误码,比如 404、500 等
                Log.e("Retrofit", "Response error: " + response.code());
            }
        }
    
        @Override
        public void onFailure(Call<List<User>> call, Throwable t) {
            // 请求失败,比如网络错误或解析错误
            Log.e("Retrofit", "Request failed", t);
        }
    });
    

    三、Retrofit + RxJava 的同步/异步与线程统一处理,用Observable发起请求

    Observable 是 RxJava 的响应式编程模型,用于处理异步数据流。其优势包括:

    • 链式调用:通过操作符(如 mapflatMap)简化复杂逻辑。
    • 线程调度:通过 subscribeOn 和 observeOn 控制线程切换。
    • 错误处理:通过 onErrorReturn 或 retry 实现容错。

    同步请求

    public interface ApiService {
        @GET("users/{id}")
        Observable<User> getUserRx(@Path("id") int id); // 返回 Observable
    }
    
    apiService.getUserRx(1)
        .subscribeOn(Schedulers.io()) // 指定请求线程
        .observeOn(Schedulers.io())   // 指定响应处理线程
        .blockingSubscribe(user -> {
            // 同步阻塞获取结果
        });

    异步请求

    // 1. 定义 API 接口,使用 RxJava 的 Observable 作为返回类型
    public interface ApiService {
        @GET("users")
        Observable<List<User>> getUsers();
    }
    
    // 2. 配置 OkHttp 客户端(可添加拦截器、日志打印等)
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            // 例如:添加日志拦截器
            // .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            .build();
    
    // 3. 配置 Retrofit,同时添加 Gson 转换器和 RxJava 适配器
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())          // JSON 数据解析
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())      // RxJava 适配器
            .build();
    
    // 4. 创建 API 服务实例
    ApiService apiService = retrofit.create(ApiService.class);
    
    // 5. 使用 RxJava 进行网络请求
    apiService.getUsers()
            .subscribeOn(Schedulers.io())                   // 在 IO 线程执行网络请求
            .observeOn(AndroidSchedulers.mainThread())      // 在主线程处理返回结果
            .subscribe(users -> {
                // 成功回调,处理用户数据
            }, throwable -> {
                // 错误回调,处理异常情况
            });
    

    方案线程管理代码复杂度适用场景
    OkHttp 原生手动切换简单请求、低耦合场景
    Retrofit 原生手动切换接口化请求、中等复杂度
    Retrofit+RxJava自动统一管理复杂异步流、高可维护性

    四、具体使用Retrofit+RxJava

    // 1. 定义服务接口
    public interface UserService {
        @POST("/user/{userId}/profile")
        Observable<BaseResponse<UserProfile>> updateProfile(
            @Path("userId") String userId,
            @Body ProfileParams params
        );
    }
    
    // 2. 发起请求
    NetworkApi.createService(UserService.class, ServiceType.User.name())
        .updateProfile("123", new ProfileParams("Kimi", "avatar.jpg"))
        .compose(NetworkApi.applySchedulers())
        .subscribe(new BaseObserver<UserProfile>() {
            @Override
            public void onSuccess(UserProfile profile) {
                // 更新UI
            }
    
            @Override
            public void onBusinessError(int code, String msg) {
                // 显示错误提示
            }
        });

    设计优势

    • 责任链模式:每个方法专注单一职责(创建→配置→调度→响应)
    • 类型安全:通过泛型确保数据模型一致性
    • 异常隔离:BaseObserver集中处理网络/业务异常
    • 线程透明:applySchedulers()隐藏线程切换细节

    该模式常见于需要支持多环境、多服务类型的现代移动应用架构,特别适合企业级应用开发中需要统一管理API请求的场景。

    这是典型的Retrofit+RxJava组合的网络请求链式调用写法,结合了工厂模式、建造者模式和响应式编程思想。其核心结构可分为四个关键部分:

    1.服务实例创建
    NetworkApi.createService(ApiService.class, ServiceType.License.name())
    • NetworkApi是自定义的工厂类,封装了Retrofit实例的创建过程
    • createService()方法通过动态代理生成ApiService接口的实现类
    • ServiceType.License.name()指定服务类型,通常用于动态配置baseUrl(如区分认证服务、业务服务等)
    2.具体API操作
    .action(id, params)
    • action()对应ApiService接口中定义的端点方法:
    @POST("/api/{serviceType}/actions")
    Observable<BaseResponse<T>> action(
        @Path("serviceType") String serviceType,
        @Body ActionParams params
    );
    • id参数可能用于路径替换(@Path注解),params作为请求体(@Body注解)
    3.线程调度组合
    .compose(NetworkApi.applySchedulers(...))
    • compose()是RxJava的操作符,统一应用线程切换规则
    • applySchedulers()典型实现:
    4.观察者封装
    new BaseObserver<BaseResponse<AccessConsentRecordDetailBean>>()
    • 自定义的BaseObserver处理通用逻辑:
    public abstract class BaseObserver<T> implements Observer<T> {
        @Override
        public void onError(Throwable e) {
            // 统一错误处理(网络异常、业务异常等)
        }
        
        @Override
        public void onNext(T response) {
            if (response.isSuccess()) {
                onSuccess(response.getData());
            } else {
                onBusinessError(response.getCode(), response.getMessage());
            }
        }
    }

    五、Flowable与Observable

    FlowableObservable是RxJava中两种核心的响应式数据流类型,结合Retrofit使用时需要根据具体场景进行选型。以下是两者的核心区别及在Retrofit中的实践建议:

    类型特性Retrofit集成场景
    Observable无背压机制的异步数据流,支持同步/异步操作,适合轻量级请求简单API调用(如获取用户信息、配置数据)
    Flowable支持背压控制的响应式流,内置5种策略处理生产消费速度差异大文件传输、实时数据推送等高并发场景

    核心区别

    1. 背压处理机制
    • Flowable
      通过BackpressureStrategy配置策略(ERROR/BUFFER/DROP/LATEST/MISSING),在Retrofit中处理大数据流时需显式指定策略:
    @GET("api/sensor-data")
    Flowable<Data> getSensorData(@Query("deviceId") String id);  // 默认使用ERROR策略[5](@ref)
    • Observable
      无背压控制,Retrofit接口直接返回Observable时需确保数据量可控,否则可能引发OOM
    2. 线程模型

    Flowable
    强制异步订阅,Retrofit需配合subscribeOn(Schedulers.io())使用:

    api.getSensorData("DEV001")
       .subscribeOn(Schedulers.io())
       .observeOn(AndroidSchedulers.mainThread())

    Observable
    支持同步调用,适合快速响应的本地缓存查询:

    @GET("api/profile")
    Observable<User> getUserProfile();  // 同步获取用户数据[9](@ref)
    3. 性能表现
    • Flowable
      因背压检测机制,吞吐量比Observable低约15-30%,但内存更安全
    • Observable
      无额外性能损耗,适合高频低数据量请求(如按钮点击事件统计)
    4.集成实现
    • BUFFER:文件下载(需注意内存监控)
    Flowable.create(emitter -> {...}, BackpressureStrategy.BUFFER)
    • DROP:实时股票行情推送(丢弃过时数据)
    • LATEST:即时聊天消息(保留最新消息)
    5.错误处理对比
    • Observable需手动捕获异常:
    .subscribe(
        data -> {...},
        error -> { // 需处理所有异常 }
    )
    • Flowable通过onBackpressureXXX操作符自动处理:
    .onBackpressureDrop(dropped -> log("丢弃数据:" + dropped))

    性能优化建议

    1. 混合使用策略
      对核心业务接口使用Flowable+BUFFER策略,非核心功能使用Observable

    2. 动态缓存控制
      通过rx2.buffer-size参数调整Flowable缓存池:

      System.setProperty("rx2.buffer-size", "256");  // 默认128[7](@ref)
    3. 生命周期管理
      使用CompositeDisposable统一释放资源:

      CompositeDisposable disposables = new CompositeDisposable();
      
      disposables.add(api.getDataStream()
          .subscribe(data -> {...}));

    通过合理选择Flowable与Observable,可使Retrofit网络层在保证稳定性的同时获得最佳性能。可以在金融交易、物联网等高频场景优先采用Flowable,而在常规业务API中使用Observable以降低复杂度。


    http://www.niftyadmin.cn/n/5869098.html

    相关文章

    UIAutomation开发常用方法的参考文档

    简介 由于UIAutomation的官方文档只有一个github中的readme文件,只是简单的使用示例,具体使用还需要在代码中查找,非常不方便。经过我多年使用UIAutomation开发的经验和整理,把常用的功能梳理成本文档,作为我的开发参考使用,这样就不用每次都翻代码了,同时也可以使用AI…

    【机器学习】强化学习(2)——捋清深度强化学习的思路

    在之前学习的过程中我了解到深度学习中很重要的一个概念是反向传播&#xff0c;最近看论文发现深度强化学习&#xff08;DRL&#xff09;有各种各样的方法&#xff0c;但是却很难区分他们的损失函数的计算以及反向传播的过程有何不同。在有监督的学习中&#xff0c;损失可以理解…

    什么是死锁?构成死锁的条件如何解决

    什么是死锁&#xff1f;构成死锁的条件&如何解决 1. 什么是死锁 在计算机科学中&#xff0c;死锁是一种非常常见且棘手的问题。从线程和锁的角度来看&#xff0c;死锁主要存在三种典型情况&#xff1a;一线程一锁、两线程两锁以及 M 线程 N 锁。接下来&#xff0c;我们将…

    微信小程序源码逆向 MacOS

    前言 日常工作中经常会遇到对小程序的渗透测试&#xff0c;微信小程序的源码是保存在用户客户端本地&#xff0c;在渗透的过程中我们需要提取小程序的源码进行问题分析&#xff0c;本篇介绍如何在苹果电脑 MacOS 系统上提取微信小程序的源码。 0x01 微信小程序提取 在苹果电…

    HTML解析 → DOM树 CSS解析 → CSSOM → 合并 → 渲染树 → 布局 → 绘制 → 合成 → 屏幕显示

    一、关键渲染流程 解析 HTML → 生成 DOM 树 浏览器逐行解析 HTML&#xff0c;构建**DOM&#xff08;文档对象模型&#xff09;**树状结构 遇到 <link> 或 <style> 标签时会暂停 HTML 解析&#xff0c;开始加载 CSS 解析 CSS → 生成 CSSOM 将 CSS 规则解析为**…

    Pycharm-Version: 2024.3.3导入conda环境

    打开一个新项目&#xff0c;点击File->Settings 找到Project->python interpreter 新增环境&#xff0c;点击add interpreter->add local interpreter 点击select existing->conda&#xff0c;选择地址为&#xff1a;anoconda/library/bin/conda.bat&#xff0c…

    Fisher散度:从信息几何到机器学习的隐藏利器

    Fisher散度&#xff1a;从信息几何到机器学习的隐藏利器 在机器学习和统计学中&#xff0c;比较两个概率分布的差异是常见任务&#xff0c;比如评估真实分布与模型预测分布的差距。KL散度&#xff08;Kullback-Leibler Divergence&#xff09;可能是大家熟悉的选择&#xff0c…

    C++ | 高级教程 | 信号处理

    &#x1f47b; 概念 信号 —— 操作系统传给进程的中断&#xff0c;会提早终止程序有些信号不能被程序捕获&#xff0c;有些则可以被捕获&#xff0c;并基于信号采取适当的动作 信号描述SIGABRT程序的异常终止&#xff0c;如调用 abortSIGFPE错误的算术运算&#xff0c;比如除…