android面试题的答案,详解 RxJava 的消息订阅和线程切换原理,安卓开发自学书籍推荐

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, “Observer onSubscribe() 所在线程为 :” + Thread.currentThread().getName());
}

@Override
public void onNext(String s) {
Log.d(TAG, “Observer onNext() 所在线程为 :” + Thread.currentThread().getName());
}

@Override
public void onError(Throwable e) {
Log.d(TAG, “Observer onError() 所在线程为 :” + Thread.currentThread().getName());
}

@Override
public void onComplete() {
Log.d(TAG, “Observer onComplete() 所在线程为 :” + Thread.currentThread().getName());
}
});
}
}.start();

输出结果为:

Thread run() 所在线程为 :Thread-2
Observer onSubscribe() 所在线程为 :Thread-2
Observable subscribe() 所在线程为 :RxCachedThreadScheduler-1
Observer onNext() 所在线程为 :main
Observer onNext() 所在线程为 :main
Observer onComplete() 所在线程为 :main

从上面的例子可以看到:

  1. Observer(观察者)的onSubscribe()方法运行在当前线程中。
  2. Observable(被观察者)中的subscribe()运行在subscribeOn()指定的线程中。
  3. Observer(观察者)的onNext()onComplete()等方法运行在observeOn()指定的线程中。

5.2 源码分析

下面我们对线程切换的源码进行一下分析,分为两部分:subscribeOn()observeOn()

5.2.1 subscribeOn()源码分析

首先来看下subscribeOn(),我们的例子中是这么个使用的:

.subscribeOn(Schedulers.io())

subscribeOn()方法要传入一个Scheduler类对象作为参数,Scheduler是一个调度类,能够延时或周期性地去执行一个任务。

5.2.1.1 Scheduler类型

通过Schedulers类我们可以获取到各种Scheduler的子类。RxJava提供了以下这些线程调度类供我们使用:

Scheduler类型使用方式含义使用场景
IoSchedulerSchedulers.io()io操作线程读写SD卡文件,查询数据库,访问网络等IO密集型操作
NewThreadSchedulerSchedulers.newThread()创建新线程耗时操作等
SingleSchedulerSchedulers.single()单例线程只需一个单例线程时
ComputationSchedulerSchedulers.computation()CPU计算操作线程图片压缩取样、xml,json解析等CPU密集型计算
TrampolineSchedulerSchedulers.trampoline()当前线程需要在当前线程立即执行任务时
HandlerSchedulerAndroidSchedulers.mainThread()Android主线程更新UI等
5.2.1.2 Schedulers类的io()

下面我们来看下Schedulers.io()的代码,其他的Scheduler子类都差不多,就不逐以分析了,有兴趣的请自行查看哈~

@NonNull
static final Scheduler IO;

@NonNull
public static Scheduler io() {
//1.直接返回一个名为IO的Scheduler对象
return RxJavaPlugins.onIoScheduler(IO);
}

static {
//省略无关代码

//2.IO对象是在静态代码块中实例化的,这里会创建按一个IOTask()
IO = RxJavaPlugins.initIoScheduler(new IOTask());
}

static final class IOTask implements Callable {
@Override
public Scheduler call() throws Exception {
//3.IOTask中会返回一个IoHolder对象
return IoHolder.DEFAULT;
}
}

static final class IoHolder {
//4.IoHolder中会就是new一个IoScheduler对象出来
static final Scheduler DEFAULT = new IoScheduler();
}

可以看到,Schedulers.io()中使用了静态内部类的方式来创建出了一个单例IoScheduler对象出来,这个IoScheduler是继承自Scheduler的。这里mark一发,后面会用到这个IoScheduler的。

5.2.1.3 Observable类的subscribeOn()

然后,我们就来看下subscribeOn()的代码:

public final Observable subscribeOn(Scheduler scheduler) {
//省略无关代码
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}

可以看到,首先会将当前的Observable(其具体实现为ObservableCreate)包装成一个新的ObservableSubscribeOn对象。 放个图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

跟前面一样,RxJavaPlugins.onAssembly()也是将ObservableSubscribeOn对象原样返回而已,这里就不看了。 可以看下ObservableSubscribeOn的构造方法:

5.2.1.4 ObservableSubscribeOn类的构造方法

public ObservableSubscribeOn(ObservableSource source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}

也就是把sourcescheduler这两个保存一下,后面会用到。

然后subscribeOn()方法就完了。好像也没做什么,就是重新包装一下对象而已,然后将新对象返回。即将一个旧的被观察者包装成一个新的被观察者。

5.2.1.5 ObservableSubscribeOn类的subscribeActual()

接下来我们回到订阅过程,为什么要回到订阅过程呢?因为事件的发送是从订阅过程开始的啊。 虽然我们这里用到了线程切换,但是呢,其订阅过程前面的内容跟上一节分析的是一样的,我们这里就不重复了,直接从不一样的地方开始。还记得订阅过程中Observable类的subscribeActual()是个抽象方法吗?因此要看其子类的具体实现。在上一节订阅过程中,其具体实现是在ObservableCreate类。但是由于我们调用subscribeOn()之后,ObservableCreate对象被包装成了一个新的ObservableSubscribeOn对象了。因此我们就来看看ObservableSubscribeOn类中的subscribeActual()方法:

@Override
public void subscribeActual(final Observer<? super T> s) {
final SubscribeOnObserver parent = new SubscribeOnObserver(s);

s.onSubscribe(parent);

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

subscribeActual()中同样也将我们自定义的Observer给包装成了一个新的SubscribeOnObserver对象。同样,放张图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后就是调用ObserveronSubscribe()方法,可以看到,到目前为止,还没出现过任何线程相关的东西,所以ObserveronSubscribe()方法就是运行在当前线程中。 然后我们重点看下最后一行代码,首先创建一个SubscribeTask对象,然后就是调用scheduler.scheduleDirect().。 我们先来看下SubscribeTask类:

5.2.1.6 SubscribeTask类

//SubscribeTask是ObservableSubscribeOn的内部类
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver parent;

SubscribeTask(SubscribeOnObserver parent) {
this.parent = parent;
}

@Override
public void run() {
//这里的source就是我们自定义的Observable对象,即ObservableCreate
source.subscribe(parent);
}
}

很简单的一个类,就是实现了Runnable接口,然后run()中调用Observer.subscribe()

5.2.1.7 Scheduler类的scheduleDirect()

再来看下scheduler.scheduleDirect()方法

public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
}

往下看:

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

//createWorker()在Scheduler类中是个抽象方法,所以其具体实现在其子类中
//因此这里的createWorker()应当是在IoScheduler中实现的。
//Worker中可以执行Runnable
final Worker w = createWorker();

//实际上decoratedRun还是这个run对象,即SubscribeTask
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

//将Runnable和Worker包装成一个DisposeTask
DisposeTask task = new DisposeTask(decoratedRun, w);

//Worker执行这个task
w.schedule(task, delay, unit);

return task;
}

我们来看下创建WorkerWorker执行任务的过程。

5.2.1.8 IoScheduler的createWorker()和schedule()

final AtomicReference pool;

public Worker createWorker() {
//就是new一个EventLoopWorker,并且传一个Worker缓存池进去
return new EventLoopWorker(pool.get());
}

static final class EventLoopWorker extends Scheduler.Worker {
private final CompositeDisposable tasks;
private final CachedWorkerPool pool;
private final ThreadWorker threadWorker;

final AtomicBoolean once = new AtomicBoolean();

//构造方法
EventLoopWorker(CachedWorkerPool pool) {
this.pool = pool;
this.tasks = new CompositeDisposable();
//从缓存Worker池中取一个Worker出来
this.threadWorker = pool.get();
}

@NonNull
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
//省略无关代码

//Runnable交给threadWorker去执行
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
}

注意,不同的Scheduler类会有不同的Worker实现,因为Scheduler类最终是交到Worker中去执行调度的。

我们来看下Worker缓存池的操作:

5.2.1.9 CachedWorkerPool的get()

static final class CachedWorkerPool implements Runnable {
ThreadWorker get() {
if (allWorkers.isDisposed()) {
return SHUTDOWN_THREAD_WORKER;
}
while (!expiringWorkerQueue.isEmpty()) {
//如果缓冲池不为空,就从缓存池中取threadWorker
ThreadWorker threadWorker = expiringWorkerQueue.poll();
if (threadWorker != null) {
return threadWorker;
}
}

//如果缓冲池中为空,就创建一个并返回。
ThreadWorker w = new ThreadWorker(threadFactory);
allWorkers.add(w);
return w;
}
}

5.2.1.10 NewThreadWorker的scheduleActual()

我们再来看下threadWorker.scheduleActual()ThreadWorker类没有实现scheduleActual()方法,其父类NewThreadWorker实现了该方法,我们点进去看下:

public class NewThreadWorker extends Scheduler.Worker implements Disposable {
private final ScheduledExecutorService executor;

volatile boolean disposed;

public NewThreadWorker(ThreadFactory threadFactory) {
//构造方法中创建一个ScheduledExecutorService对象,可以通过ScheduledExecutorService来使用线程池
executor = SchedulerPoolFactory.create(threadFactory);
}

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
//这里的decoratedRun实际还是run对象
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
//将decoratedRun包装成一个新对象ScheduledRunnable
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);

//省略无关代码

if (delayTime <= 0) {
//线程池中立即执行ScheduledRunnable
f = executor.submit((Callable)sr);
} else {
//线程池中延迟执行ScheduledRunnable
f = executor.schedule((Callable)sr, delayTime, unit);
}

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

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

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

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

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

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

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

频+大厂面试真题+项目实战源码》]( )收录**

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值