Android进阶之Rxjava的使用及原理梳理

本文详细介绍了RxJava在Android开发中的应用,包括使用场景、配合Retrofit的方式、线程切换原理以及操作符的使用。通过案例展示了如何利用RxJava进行图片下载、功能防抖、注册登录等业务,并探讨了RxJava的观察者模式、Hook模式、装饰模型和背压等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、RxJava的使用场景

1 RxJava 的思维
GitHub 上的解说,RxJava 是基于事件流的链式调用、实现异步操作的库。

2 RxJava使用场景导入
RxJava 是一种扩展的观察者模式,包含以下几个角色:

角色作用
被观察者产生事件
观察者接收事件,并给出响应动作
订阅连接 被观察者 & 观察者

以上角色的关系是:被观察者 (Observable) 通过订阅(Subscribe) 按顺序发送事件 给观察者 (Observer), 观察者(Observer) 按顺序接收事件并作出对应的响应动作。
其间的事件可设置多个,一个一个按顺序发送处理。

通过一个下载图片的案例来说明:
传统方式下载图片可以用new Thread, 或者线程池或者更多其它方式,程序员思维不一样能写出很多种方式,所以引入Rxjava思维编程下载(统一风格),以下是代码:

 // 网络图片的链接地址
private final static String PATH = "http://pic1.win4000.com/wallpaper/c/53cdd1f7c1f21.jpg";
// 弹出加载框
private ProgressDialog progressDialog;
// ImageView控件,用来显示结果图像
private ImageView image;

 public void rxJavaDownloadImageAction(View view) {
        // TODO 起点
        Observable.just(PATH) //TODO 第二步 内部分发
                // TODO 第三步
                .map(new Function<String, Bitmap>() {
                    @Override
                    public Bitmap apply(String s) throws Exception {
                        URL url = new URL(PATH);
                        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                        httpURLConnection.setConnectTimeout(5000);
                        int responseCode = httpURLConnection.getResponseCode(); // 才开始 request
                        if (responseCode == HttpURLConnection.HTTP_OK) {
                            InputStream inputStream = httpURLConnection.getInputStream();
                            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                            return bitmap;
                        }
                        return null;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                 // 订阅 起点 和 终点 订阅起来
                .subscribe(
                        // TODO 终点
                        new Observer<Bitmap>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        // 预备 开始 要分发
                        // TODO 第一步
                        progressDialog = new ProgressDialog(DownloadPicActivity.this);
                        progressDialog.setTitle("download run");
                        progressDialog.show();
                    }

                    // TODO 第四步 拿到事件
                    @Override
                    public void onNext(Bitmap bitmap) {
                        image.setImageBitmap(bitmap);
                    }

                    // 错误事件
                    @Override
                    public void onError(Throwable e) {}

                    // TODO 第五步:完成事件
                    @Override
                    public void onComplete() {
                        if (progressDialog != null)
                            progressDialog.dismiss();
                    }
                });
    }

这就是一个简单的图片下载的流程,流程不是重点,重点是如果后续要在这个功能上添加其他辅助功能会很方便,比如对图片做处理加水印,不需要重新构建代码,只需找到已下载好的图片事件,并在这事件后再添加加水印甚至其他更多的功能即可。

原有代码基础上(.subscribeOn分配线程前) 添加以下代码, Function中的第一个泛型类型是上面的事件返回类型,第二个泛型是当前事件返回类型:

   .map(new Function<Bitmap, Bitmap>() {
            @Override
            public Bitmap apply(Bitmap bitmap) throws Exception {
                Paint paint = new Paint();
                paint.setTextSize(88);
                paint.setColor(Color.RED);
                return drawTextToBitmap(bitmap, "水印效果",paint, 88 , 88);
            }
        })
 // 图片上绘制文字 加水印
    private final Bitmap drawTextToBitmap(Bitmap bitmap, String text, Paint paint, int paddingLeft, int paddingTop) {
        Bitmap.Config bitmapConfig = bitmap.getConfig();

        paint.setDither(true); // 获取跟清晰的图像采样
        paint.setFilterBitmap(true);// 过滤一些
        if (bitmapConfig == null) {
            bitmapConfig = Bitmap.Config.ARGB_8888;
        }
        bitmap = bitmap.copy(bitmapConfig, true);
        Canvas canvas = new Canvas(bitmap);

        canvas.drawText(text, paddingLeft, paddingTop, paint);
        return bitmap;
    }

3 配合Retrofit使用场景
3.1 Retrofit + RxJava 基本使用demo
首先,和 RxJava 配合使用时 retrofit 是一个管理者,即 被观察者 – retrofit – 观察者,所以将retrofit封装:

public class RetrofitFactory {
    public static String BASE_URL = "https://www.wanandroid.com/";

    public static void setBaseUrl(String baseUrl) {
        BASE_URL = baseUrl;
    }

    /**
     * 根据各种配置创建出Retrofit
     *
     * @return 返回创建好的Retrofit
     */
    public static Retrofit getOnlineCookieRetrofit() {
        // OKHttp客户端
        OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
        // 各种参数配置
        OkHttpClient okHttpClient = httpBuilder
              //  .addNetworkInterceptor(new StethoInterceptor())
                .readTimeout(10000, TimeUnit.SECONDS)
                .connectTimeout(10000, TimeUnit.SECONDS)
                .writeTimeout(10000, TimeUnit.SECONDS)
                .build();

        return new Retrofit.Builder().baseUrl(BASE_URL)
                // TODO 请求用 OKhttp
                .client(okHttpClient)
                // TODO 响应RxJava
                // 添加一个json解析的工具
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                // 添加rxjava处理工具
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
}

然后通过接口分别查询类型数据和根据对应类型id查详细列表信息:

api = RetrofitFactory.getOnlineCookieRetrofit().create(WanAndroidAPI.class);

 // Retrofit+RxJava 查询 项目分类  (总数据查询)
    public void getProjectAction(View view) {
        // 获取网络API
        api.getProject()
                .subscribeOn(Schedulers.io()) // 上面 异步
                .observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
                .subscribe(new Consumer<ProjectBean>() {
                    @Override
                    public void accept(ProjectBean projectBean) throws Exception {
                        Log.d(TAG, "accept: " + projectBean); // UI 可以做事情
                    }
                });
    }

    // Retrofit+RxJava 查询  项目分类的id 如294  获取项目列表数据  (Item)
    public void getProjectListAction(View view){
        // 注意:这里的 294 是项目分类 所查询出来的数据
        // id 写死的
        api.getProjectItem(1, 294)
                // .....
                .subscribeOn(Schedulers.io()) // 上面 异步
                .observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
                .subscribe(data->{
                    Log.d(TAG, "getProjectListAction: " + data);
                });
    }

以上测试能正常获取对应数据,然后下一步就是通过点击事件一次性获取所有数据。

3.2 功能防抖
因考虑到用户有可能多次点击按钮导致重复发请求获取数据,为避免此现象,添加功能防抖处理。

build.gradle中添加依赖:

implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' // 操作功能防抖
  // 对哪个控件防抖动
Button bt_anti_shake = findViewById(R.id.bt_anti_shake);
+ RxView.clicks(bt_anti_shake)
+         .throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次
         .subscribe(new Consumer<Object>() {
             @Override
             public void accept(Object o) throws Exception {
                 api.getProject() // 查询主数据
                         .subscribeOn(Schedulers.io())
                         .observeOn(AndroidSchedulers.mainThread())
                         .subscribe(new Consumer<ProjectBean>() {
                             @Override
                             public void accept(ProjectBean projectBean) throws Exception {
                                 for (ProjectBean.DataBean dataBean : projectBean.getData()) { // 10
                                     // 查询item数据
                                     api.getProjectItem(1, dataBean.getId())
                                             .subscribeOn(Schedulers.io())
                                             .observeOn(AndroidSchedulers.mainThread())
                                             .subscribe(new Consumer<ProjectItem>() {
                                                 @Override
                                                 public void accept(ProjectItem projectItem) throws Exception {
                                                     Log.d(TAG, "accept: " + projectItem); // 可以UI操作
                                                 }
                                             });
                                 }
                             }
                         });
             }
         });

但是,以上代码嵌套了多次网络请求,可读性差,需修改。

3.3 网络嵌套 flatMap

 private void antiShakeActionUpdate() {
        Button bt_anti_shake = findViewById(R.id.bt_anti_shake);
        RxView.clicks(bt_anti_shake)
                .throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次
                // 只给下面 切换 异步
                .observeOn(Schedulers.io())
                .flatMap(new Function<Object, ObservableSource<ProjectBean>>() {
                    @Override
                    public ObservableSource<ProjectBean> apply(Object o) throws Exception {
                        return api.getProject(); // 主数据
                    }
                })
                .flatMap(new Function<ProjectBean, ObservableSource<ProjectBean.DataBean>>() {
                    @Override
                    public ObservableSource<ProjectBean.DataBean> apply(ProjectBean projectBean) throws Exception {
                        return Observable.fromIterable(projectBean.getData()); // 自己搞一个发射器 发多次 10
                    }
                })
                .flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() {
                    @Override
                    public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception {
                        return api.getProjectItem(1, dataBean.getId());
                    }
                })
                .observeOn(AndroidSchedulers.mainThread()) // 给下面切换 主线程
                .subscribe(new Consumer<ProjectItem>() {
                    @Override
                    public void accept(ProjectItem projectItem) throws Exception {
                        Log.d(TAG, "accept: " + projectItem);
                    }
                });
    }

3.4 doOnNext
比如完成注册业务成功后马上去做登录业务的处理用一行代码来完成的话可通过 doOnNext 实现。

3.5 销毁
onDestroy中添加如下代码:

@Override
    protected void onDestroy() {
        super.onDestroy();
        if (disposable != null)
            if (!disposable.isDisposed())
                disposable.dispose();
    }

4 RxJava2 升级到RxJava3
目前版本已更新到 3.0.4,从 RxJava2.0 升级到 RxJava3.0 添加依赖处的修改:

- //  api "io.reactivex.rxjava2:rxjava:2.2.12"
+    api "io.reactivex.rxjava3:rxjava:3.0.4"
    //rxandroid
-    //api "io.reactivex.rxjava2:rxandroid:2.1.1"
+   api "io.reactivex.rxjava3:rxandroid:3.0.0"
    api "com.squareup.okhttp3:okhttp:3.12.3"
    // Retrofit库
    // Retrofit网络处理
    api "com.squareup.retrofit2:retrofit:2.7.2"
    api "com.squareup.retrofit2:converter-scalars:2.7.2"
    // Retrofit的gson库
    api "com.squareup.retrofit2:converter-gson:2.7.2"
    // Retrofit的rx解析库
-  // api "com.squareup.retrofit2:adapter-rxjava2:2.7.2"
+  implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'

参考RxJava 适配器改造,代码中重新导包后还需修改Retrofit初始化处:

+ val rxAdapter = RxJava3CallAdapterFactory.create()
val retrofit : Retrofit = Retrofit.Builder()
        .baseUrl("https://www.wanandroid.com")
        // 请求方
        .client(mOkHttpClient)
        // 响应方
-       // .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+        .addCallAdapterFactory(rxAdapter)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
return retrofit.create(apiInterface)

二、Rxjava模式及原理

1 标准的观察者模式

2 Rxjava模式(Hook)---- 很多操作都会进入全局监听 onAssembly

3 map变换操作符

4 装饰模型

5 背压

三、 Rxjava的线程切换

RxJava 线程控制是指给观察者和被观察者指定工作线程类型,一般情况下,被观察者在子线程中生产事件(如实现耗时操作),观察者在子线程中接收/响应事件(实现UI操作)。
RxJava中内置了多种调度类型:

类型应用场景
Schedulers.io()io操作线程—网络请求、读写文件等io密集型操作
Schedulers.computation()CPU计算操作线程—大量计算操作
Schedulers.newThread()常规新线程—耗时等操作
Schedulers.trampoline()
Schedulers.single()
AndroidSchedulers.mainThread()Android主线程—操作UI

1 subscribeOn — 向上分发,指定上面代码的工作线程类型
若Observable.subscribeOn()多次指定被观察者 生产事件的线程,则只有第一次指定有效,其余的指定线程无效。

2 ObserveOn — 向下分发,指定下面代码的工作线程类型
若Observable.observeOn()多次指定观察者接收及响应事件的线程,则每次指定均有效,即每指定一次都会进行一次线程的切换。

四、RxJava 操作符

1 自定义操作符

2 操作符的分类及使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值