Rxjava 2,高级安卓面试题及答案2024

本文深入解析了RxJava2中Observable#subscribeOn(Scheduler)的工作原理,通过源码分析展示了如何通过scheduleDirect方法和Worker线程来控制订阅操作的执行线程。文章还探讨了为何只有第一次subscribeOn设置有效,并提供了示例代码进行说明。

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

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

static final class NewThreadTask implements Callable {

@Override

public Scheduler call() throws Exception {

return NewThreadHolder.DEFAULT;

}

}

static final class NewThreadHolder {

static final Scheduler DEFAULT = new NewThreadScheduler();

}

public static Scheduler io() {

return RxJavaPlugins.onIoScheduler(IO);

}

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

static final class IOTask implements Callable {

@Override

public Scheduler call() throws Exception {

return IoHolder.DEFAULT;

}

}

static final class IoHolder {

static final Scheduler DEFAULT = new IoScheduler();

}

static final class IoHolder {

static final Scheduler DEFAULT = new IoScheduler();

}

我们再回到 ObservableSubscribeOn 的 subscribeActual 方法,在上一篇博客的时候已经讲解 Observable 和 Observer 之间是怎样实现订阅关系的,这里就不再具体展开了。

接下来,我们重点关注这一行代码

parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));

我们先来看一下 SubscribeTask 这个类,他是 ObservableSubscribeOn 的一个非静态内部类,可以看到 其实也比较简单,他实现了 Runnable 接口,并且持有 parent 引用。

final class SubscribeTask implements Runnable {

private final SubscribeOnObserver parent;

SubscribeTask(SubscribeOnObserver parent) {

this.parent = parent;

}

@Override

public void run() {

source.subscribe(parent);

}

}

然后在 run 方法中,通过 source.subscribe(parent) 建立联系。因而,当我们的 SubscribeTask 的 run 方法运行在哪个线程,相应的 observer 的 subscribe 方法就运行在哪个线程。

这里可能会有人有疑问,SubscribeTask 没有 source 属性,它是怎么访问到 ObservableSubscribeOn 的属性的。

我们知道 java 中,非静态内部类默认持有外部类的引用,因而他可以正常访问外部类 ObservableSubscribeOn 的 source 属性。


接着,我们再来看一下 scheduler.scheduleDirect 这个方法

@NonNull

public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {

final Worker w = createWorker();

// 判断 run 是否为 null

final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

DisposeTask task = new DisposeTask(decoratedRun, w);

w.schedule(task, delay, unit);

return task;

}

  • 首先,创建一个 Worker w

  • 第二步,DisposeTask 将 decoratedRun 包装起来

  • 第三步:w 去调度 task

这里我们以 NewThreadScheduler 为例,来看看这个 Worker 到底是什么?

public Worker createWorker() {

return new NewThreadWorker(threadFactory);

}

public class NewThreadWorker extends Scheduler.Worker implements Disposable {

private final ScheduledExecutorService executor;

volatile boolean disposed;

public NewThreadWorker(ThreadFactory threadFactory) {

executor = SchedulerPoolFactory.create(threadFactory);

}


}

public static ScheduledExecutorService create(ThreadFactory factory) {

final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);

if (PURGE_ENABLED && exec instanceof ScheduledThreadPoolExecutor) {

ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec;

POOLS.put(e, exec);

}

return exec;

}

从上面可以看到,其实 worker 里面封装了 executor(线程池),看到这里,相信你也基本明白 Rxjava 线程切换的原理了,其实很简单。

在 ObservableSubscribeOn subscribeActual 方法中, SubscribeTask 包装 parent(SubscribeOnObserver ,包装了 Observer),SubscribeTask 实现了 Runnable 接口,在 run 方法里面调用了 source.subscribe(parent),因而 run 方法所执行的线程将由 worker 决定。这就是 下游决定上游 observable 执行线程的原理。

接下来我们再来看一下:DisposeTask

static final class DisposeTask implements Disposable, Runnable, SchedulerRunnableIntrospection {

final Runnable decoratedRun;

final Worker w;

Thread runner;

DisposeTask(Runnable decoratedRun, Worker w) {

this.decoratedRun = decoratedRun;

this.w = w;

}

@Override

public void run() {

runner = Thread.currentThread();

try {

decoratedRun.run();

} finally {

dispose();

runner = null;

}

}

@Override

public void dispose() {

if (runner == Thread.currentThread() && w instanceof NewThreadWorker) {

((NewThreadWorker)w).shutdown();

} else {

w.dispose();

}

}

@Override

public boolean isDisposed() {

return w.isDisposed();

}

@Override

public Runnable getWrappedRunnable() {

return this.decoratedRun;

}

}

}

// 将 新的 Disposable 设置给 parent ,方便取消订阅关系,

//(因为我们对 Observer 进行相应的包装,原来的 parent 的 Disposable 已经不能代表最新的 Disposable)

parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));

DisposeTask 实现了 Disposable,Runnable ,SchedulerRunnableIntrospection 接口,Disposable 接口主要是用来取消订阅关系的 Disposable。


Observable#subscribeOn(Scheduler) 第一次有效原理

Observable.create(new ObservableOnSubscribe() {

@Override

public void subscribe(ObservableEmitter emitter) throws Exception {

Log.i(TAG, “subscribe: getName=” +Thread.currentThread().getName());

emitter.onNext(“1”);

emitter.onNext(“2”);

emitter.onNext(“3”);

emitter.onComplete();

}

}) // 进行两次 subscribeOn

.subscribeOn(Schedulers.io()).subscribeOn(Schedulers.computation()).subscribe(new Observer() {

@Override

public void onSubscribe(Disposable d) {

Log.e(“TAG”, "onSubscribe(): ");

}

@Override

public void onNext(String s) {

Log.e(“TAG”, "onNext(): " + s);

}

@Override

public void onError(Throwable e) {

}

@Override

public void onComplete() {

Log.e(“TAG”, "onComplete(): ");

}

});

subscribe: getName=RxCachedThreadScheduler-1

如果将上述的 subscribeOn 的顺序置换

subscribeOn(Schedulers.computation()).subscribeOn(Schedulers.io())

那么将打印出

subscribe: getName=RxComputationThreadPool-1

为什么是第一次 Observable#subscribeOn(Scheduler) 才有效呢?

前面我们分析到,Observable#subscribeOn(Scheduler) 实际上是将 Observable#subscribe(Observer) 的操作放在了指定线程,当我们调用 subcribe 的时候,它的过程是从下往上的,即下面的 Observable 调用上面的 Observanle。

所以对于我们上面的第一个例子,他的调用流程是这样的:第三个 Observable 调用 Observable#subscribe(Observer) 启动订阅,在其内部会激活第二个 Observable 的 Observable#subscribe(Observer) 方法,但是此时该方法外部被套入了一个 Schedulers.computation() 线程

于是这个订阅的过程就被运行在了该线程中。用伪代码演示如下

public class Observable {

// 第「二」个 Observable

Observable source;

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

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

上述【高清技术脑图】以及【配套的架构技术PDF】点击:Android架构视频+BAT面试专题PDF+学习笔记,或者私信回复【技能提升】即可获取!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

IblDm-1710672436865)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-N9KvwsEI-1710672436866)]

最后

现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

上述【高清技术脑图】以及【配套的架构技术PDF】点击:Android架构视频+BAT面试专题PDF+学习笔记,或者私信回复【技能提升】即可获取!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!

Android架构师之路很漫长,一起共勉吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值