Trace.traceEnd(traceTag);
}
}
…
msg.recycleUnchecked();
}
}
这是循环消息时的部分代码,处理消息代码是msg.target.dispatchMessage(msg);
,这里的target就是当时发送消息的handler。
3.在子线程发送消息,却能够在主线程接收消息,主线程和子线程是怎么样切换的?
子线程用handler发送消息,发送的消息被送到与主线程相关联的MessageQueue,也是主线程相关联的Looper在循环消息,handler所关联的是主线程的Looper和MessageQueue,所以最后消息的处理逻辑也是在主线程。只有发送消息是在子线程,其它都是在主线程,Handler与哪个线程的Looper相关联,消息处理逻辑就在与之相关的线程中执行,相应的消息的走向也就在相关联的MessageQueue中。所以子线程切换到主线程是很自然的过程,并没有想象中的复杂。
4.能不能在子线程中创建Handler?
可以,但是在创建前先调用prepare()方法创建Looper。Handler创建的时候,会去检查是否有创建Looper,如果没有创建就会抛出异常。相关源码如下:
public Handler(Callback callback, boolean async) {
…
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can’t create handler inside thread " + Thread.currentThread()
- " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
所以我们在子线程需要先调用prepare()方法创建Looper。这里还多提一点,在主线程创建就不需要自己创建Looper,因为在ActivityTread类里面,已经为我们创建好了,相关源码如下:
public static void main(String[] args) {
…
Looper.prepareMainLooper();// 为主线程创建looper
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format “seq=114”
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}
Looper.prepareMainLooper();
这句代码就是为主线程创建Looper。 所以在主线程中直接创建一个Handler,就直接可以循环消息,因为安卓的主线程已经为我们准备好了Looper。
5.一个线程可以有几个Handler?几个Looper?
一个线程可以有多个Handler,但是只有一个Looper。创建Handler之前,需要创建Looper,否则会报错。源码里面已经做了说明。
public Handler(Callback callback, boolean async) {
…
mLooper = Looper.myLooper();
if (mLooper == null) {//判断Looper是否被创建
throw new RuntimeException(
"Can’t create handler inside thread " + Thread.currentThread()
- " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
再来看Looper的创建,是在prepare()方法里。
// sThreadLocal.get() will return null unless you’ve called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed));
}
在创建之前去判断looper是否存在,存在就会抛出Only one Looper may be created per thread
异常,这是在告诉我们一个线程只能有一个Looper。而TreadLocal的作用就是线程间隔离,确保一个线程对应一个Looper。还可以看看Looper构造方法的源码
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue的初始化是在Looper的构造方法里。不管一个线程有多少个Handler,相关联的都是同一个Looper和MessageQueue。
关于Handler,可以问的问题有很多,以上只是抽出一些我认为比较重要的问题。在寻找答案以后,我将Handler机制的整个过程在脑海中过了一遍,并且画了个草图。
Handler机制中重要类的相互关联图
Handler机制原理涉及几个重要的类:Handler、Message、MessageQueue、Looper。
就用子线程向主线程发送消息来说明整个过程。
首先在主线程创建一个Handler,在Handler类里面会创建Looper以及MessageQueue的对象,并且在Handler构造方法里面赋值
final Looper mLooper;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
目前已经更新的部分资料:
厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-FUeUhaZm-1711726429623)]
最后
目前已经更新的部分资料:
[外链图片转存中…(img-Ct02PMRE-1711726429623)]
[外链图片转存中…(img-umjbYdqs-1711726429623)]
[外链图片转存中…(img-LjgvylZz-1711726429624)]