你可以不用RxJava,但必须得领悟它的思想!_rxjava 还 有人用吗

1.ObervableXXX的整体认识

其实这些 ObservableXXX 就像工厂中的一个个流水线组装货物的机器,货物从 起点终点 传输,当货物传输到不同的机器上时,这台机器会操作货物完成这一层的组装工作,最后会将其传输到下一个机器上去,完成那个机器的组装任务,直至传输到 终点 整个货物组装完毕。

回头看上面的例子,我们加入的两张卡片就像是两台机器一样,map 机器完成水印工作后,将 货物 扔给后一个 map 机器,由它完成高斯模糊工作,最后 货物组装完成,被传输到了终点,在这里我们就能拿到最终期望的结果。

接下来我会举两个例子,带着大家看看它们的内部逻辑。因为几乎所有的 ObservableXXX 的逻辑和功能都相似,因此大家在看完下面几个例子后,可以自行去源码中轻松查看你想看的部分。

2.ObservableMap内部运转逻辑

这样一说相信大家对它有了一个整体的感受,那么我们现在以 ObservableMap 为例,重点讲解一下内部逻辑吧。

⚡ 在讲解源码之前,我们需要先知道一个点:代码当中的 source 指的都是上一个操作符返回的 Observable 对象,function 指的是调用此操作符需要传递的参数,即匿名实现类,另外下文还会出现 自定义source,这个就是我们在使用 create操作符 时传递的参数,也称 顶层source ,它是货物运输的 起点

// ObservableMap.java

// Observable继承自AbstractObservableWithUpstream,AbstractObservableWithUpstream又继承Observable
// Observable实现了ObservableSource接口
// 因此这里会重写 subscribe 方法
public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {
final Function<? super T, ? extends U> function;

public ObservableMap(ObservableSource source, Function<? super T, ? extends U> function) {
// source 是上一层操作符返回的Observable对象
// function 是我们使用此操作符传递的那个匿名实现类,<可以称其为这一个机器需要做的操作>
// 在本类中其实就是实现了Function接口的匿名类,里边有个方法叫作apply
// <本层机器处理货物时>,会看到apply方法
super(source);
this.function = function;
}

// 此方法是 subscribe 的具体实现方法
// 我们进行订阅时,即调用subscribe时,最终会调用此方法,<最终通知起点开始传输货物>
@Override
public void subscribeActual(Observer<? super U> t) {
// t代表我们调用subscribe时传递的参数,即匿名实现类,<我们可以称其为后一台机器要执行的操作>
// 将t和function包装一下,再调用 source 的 subscribe,<也就是前面那台机器的subscribe>
// 这里的subscribe可以理解为通知上层机器开始运输货物
source.subscribe(new MapObserver<T, U>(t, function));
}

// 内部类,<将后一台机器执行的操作和上一个机器绑定起来>
// 就是将后一个机器的操作做了层封装,包裹了一层
static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> {
final Function<? super T, ? extends U> mapper;

MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) {
super(actual);
this.mapper = mapper;
}

// <此方法是货物到达本机器时,本机器的相应操作>
@Override
public void onNext(T t) {
// 这些判空直接不看
if (done) {
return;
}

if (sourceMode != NONE) {
actual.onNext(null);
return;
}

U v;
// 这里会执行apply方法,是我们使用map操作符传递的实现了Function接口的匿名内部类中的方法
// <本层机器开始处理上层机器传递过来的货物>
try {
v = ObjectHelper.requireNonNull(mapper.apply(t), “The mapper function returned a null value.”);
} catch (Throwable ex) {
fail(ex);
return;
}
// 本层机器的货物处理完毕,扔给下层机器继续处理
// <actual代表下层机器>
actual.onNext(v);
}
}

总的来说 ObservableMap 做了两件事,第一就是调用 subscribeActual 通知上层机器让其开始传递货物,第二就是调用 onNext 处理传递到本层的货物,最后再扔给下一个机器去处理。

其实 ObservableXXX 结构和逻辑都差不太多,我将最开始给的例子用图表示了出来:

相信大家可以从这张图中看出,整个运转逻辑是一个 U型结构,当我们调用 subscribe 时,就会通知 起点 运输货物,其实也就是设置了观察者。然后,货物就会一层一层往下传递,最终流到我们的终点。如果我们中途想继续添加需求,直接往中间那个过程添加机器(或者说是卡片)就行了,非常方便,而且整个链式调用下来,代码会比不用 RxJava 显得更加干净整洁易读。

3.ObservableCreate内部运转逻辑

那么起点是怎么接收下面的 机器 传递上来的运输货物的信号并开始运输货物的呢?

public final class ObservableCreate extends Observable {
// 我们自己写的source
final ObservableOnSubscribe source;

public ObservableCreate(ObservableOnSubscribe source) {
// 这个source是我们自定义的,也就是使用create操作符时传递的那个参数
// 这就是我们的所说的起点、源头
this.source = source;
}

// 这个就是起点触发传送任务的方法
@Override
protected void subscribeActual(Observer<? super T> observer) {
CreateEmitter parent = new CreateEmitter(observer);
// 这个observer是从下面机器经过一层层包装后传递过来的observer
// 实际上调用的是我们使用subscribe传递参数当中的onSubscribe方法,即终点的onSubscribe方法
observer.onSubscribe(parent);

// 开始传输任务,将货物往下传递
try {
// 这里的source就是顶层的自定义source
source.subscribe(parent);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
parent.onError(ex);
}
}

// 这个就是发射器,用于将货物从起点往下传递的工具
// 通常情况下,我们在自定义source中都会调用此类的onNext方法开始向下传输货物
static final class CreateEmitter
extends AtomicReference
implements ObservableEmitter, Disposable {
private static final long serialVersionUID = -3434801548987643227L;

final Observer<? super T> observer;

CreateEmitter(Observer<? super T> observer) {
this.observer = observer;
}

// 向下传输货物的方法
@Override
public void onNext(T t) {

if (!isDisposed()) {
// 将货物扔给下一层机器去处理
observer.onNext(t);
}
}

}

}

至此,两个典型的 ObservableXXX 讲解完毕,可能大家对源码当中的 sourcesubscribeActual 中的参数 observer/t 傻傻分不清楚,不知道这些是指 前一个机器 ,还是 后一个机器 ,那么接下来请看下面这幅图。

source 是前一个操作符返回的 Observable 对象,这个相信大家都容易理解。这里着重说一下上述图的下半部分,大家还记得终点是怎么通知起点开始传输 货物 的吗?没错,是通过每一个 Observable.subscribeActual 方法 (机器)中的参数不断向起点传递,每个 subscribeActual 方法中都会调用 source.subscribe(),也就是通知前面机器传输包裹,每向前传递一次就 封一层包裹 ,最后触发 起点 传输货物,之后就开始一层层 拆包裹 ,最终拆到终点(subscribe)中我们自己实现的 ObservableOnSubscribe 中。

小结

为了便于大家理解前面所讲述的知识点,这里我们用文章开头的那个例子继续深入讲解,我们来看上述代码是如何执行的,再贴一遍代码:

Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext();
}
}).map(new Function<Bitmap, Bitmap>() {
@Override
public Object apply(Bitmap bitmap) throws Exception {
// todo
}
}).map(new Function<Bitmap, Bitmap>() {
@Override
public ObservableSource<?> apply(Bitmap bitmap) throws Exception {
// todo
}
}).subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {}

@Override
public void onNext(Bitmap bitmap) {}

@Override
public void onError(Throwable e) {}

@Override
public void onComplete() {}
});

1️⃣ 我们通过 .subscribe(),发起了订阅,开始观察被观察者,结合我们之前讲的,我们实际上调用的是map 返回对象中的 subscribe 方法,即 ObservableMap

// ObservableMap.java

//它会继续调用上层机器的subscribe,通知上层机器传输货物
public void subscribeActual(Observer<? super U> t) {
// 封包裹,传递给上层机器
source.subscribe(new MapObserver<T, U>(t, function));
}

2️⃣ 这里它会继续调用上层机器的 subscribe,由于上个操作符仍是 map,所以我们直接跳过,看 create 操作符。

// ObservableCreate.java

protected void subscribeActual(Observer<? super T> observer) {
CreateEmitter parent = new CreateEmitter(observer);
// 这里是调用下一层机器的onSubscribe方法
observer.onSubscribe(parent);

try {
// 调用我们自定义的soruce,开始传输货物
// 例子中我们自定义soruce中写的代码是emitter.onNext,开始传输货物
source.subscribe(parent);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
parent.onError(ex);
}
}

3️⃣ 到这里,我们已经通知 起点 发起传送任务,那么接下来就是传输货物的过程,逐步 拆包 的过程。随着货物的运输,代码会不断调用最外层包裹中的 observable 对象的 onNext 方法,看起来就像在拆包裹一样,这其实也是前面说的运输包裹的过程,如此反复,最后到达我们的终点的 onNext 方法,我们拿到最终期望的货物,至此,整个过程基本完毕。

✔️ 现在是不是脑海里面又出现了前面所说的 U型结构 呢?

RxJava是如何完成线程切换的?

RxJava 能完成线程切换是通过 subscribeOnobserveOn。正常情况下我们都是这么用的:

observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(…)

前面反复强调整个框架的执行过程是一个 U型结构,请大家思考一下,subscribeOnobserveOn 切换线程的核心代码是在 subscribeActual 中还是 onNext 中?

回忆一下,我们通知 起点 开始传输货物时,是通过 subscribeActual 逐层往上传递的,紧接着就开始运输货物处理货物了,就比如 map 机器中的 apply 方法就是处理货物的具体方法。因此,这一段任务都应该在 子线程 中完成,所以 subscribeOn的切换线程的核心代码是在subscribeActual中的。

observeOn 的作用就是让终点的 onNext 方法中的代码在主线程中执行,那么理应只需要在 observeOn操作符中的onNext中切换线程即可。

有了上述猜想,我们现在进入源码验证一下。

1.subscribeOn的线程切换

// 不仅有io线程,还有以下这些线程

SINGLE = RxJavaPlugins.initSingleScheduler(new SingleTask());

COMPUTATION = RxJavaPlugins.initComputationScheduler(new ComputationTask());

IO = RxJavaPlugins.initIoScheduler(new IOTask());

TRAMPOLINE = TrampolineScheduler.instance();

NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());

我们首先来看看使用该操作符时传递的参数 Schedulers.io() 到底是个什么东西。我们一层层点进去最后会发现它其实就是 new了一个线程池 :

// 一路点进去,会发现其实就是new了一个线程池
CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT, threadFactory);

其中的 CacheWorkerPool 的源码如下:

// IoSchedule.java,这个类将线程池中的创建、调度、执行等方法做了一下封装

// 这个方法就是得到线程池
public Worker createWorker() {
// EventLoopWorker继承自Worker,是对线程池做了一下封装,包裹了一层
return new EventLoopWorker(pool.get());
}

// 实际调用线程池的方法
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
if (tasks.isDisposed()) {
// don’t schedule, we are unsubscribed
return EmptyDisposable.INSTANCE;
}
// 里面就是调用线程池的submit等方法
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}

// CacheWorkerPool是IoSchedule中的内部类
CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
this.keepAliveTime = unit != null ? unit.toNanos(keepAliveTime) : 0L;
this.expiringWorkerQueue = new ConcurrentLinkedQueue();
this.allWorkers = new CompositeDisposable();
this.threadFactory = threadFactory;

ScheduledExecutorService evictor = null;
Future<?> task = null;
if (unit != null) {
// 这里的核心线程数是1就足够了,因为我们子线程中的任务都是链式调用的

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ufWi-1712743561007)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-zblhuC6U-1712743561008)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值