下面的这幅图很完整的表现了整个handler
机制。
要理解handler的实现原理,其实最重要的是理解Looper
的实现原理,Looper
才是实现handler
机制的核心。任何一个handler
在使用sendMessage
或者post
时候,都是先构造一个Message
,并把自己放到message中
,然后把Message
放到对应的Looper
的MessageQueue
,Looper
通过控制MessageQueue
来获取message
执行其中的handler
或者runnable
。 要在当前线程中执行handler
指定操作,必须要先看当前线程中有没有looper
,如果有looper
,handler
就会通过sendMessage
,或者post
先构造一个message
,然后把message
放到当前线程的looper
中,looper
会在当前线程中循环取出message
执行,如果没有looper
,就要通过looper.prepare()
方法在当前线程中构建一个looper
,然后主动执行looper.loop()
来实现循环。
梳理一下其实最简单的就下面四条:
1、每一个线程中最多只有一个Looper
,通过ThreadLocal
来保存,Looper
中有Message
队列,保存handler
并且执行handler
发送的message
。
2、在线程中通过Looper.prepare()
来创建Looper
,并且通过ThreadLocal
来保存Looper
,每一个线程中只能调用一次Looper.prepare()
,也就是说一个线程中最多只有一个Looper
,这样可以保证线程中Looper
的唯一性。
3、handler
中执行sendMessage
或者post
操作,这些操作执行的线程是handler
中Looper
所在的线程,和handler
在哪里创建没关系,和Handler
中的Looper
在那创建有关系。
4、一个线程中只能有一个Looper
,但是一个Looper
可以对应多个handler
,在同一个Looper
中的消息都在同一条线程中执行。
2、Handler机制,sendMessage和post(Runnable)的区别?
要看sendMessage
和post
区别,需要从源码来看,下面是几种使用handler
的方式,先看下这些方式,然后再从源码分析有什么区别。 例1、 主线程中使用handler
//主线程
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 1) {
//doing something
}
return false;
}
});
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
上面是在主线程中使用handler
,因为在Android
中系统已经在主线程中生成了Looper
,所以不需要自己来进行looper
的生成。如果上面的代码在子线程中执行,就会报
Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()
如果想着子线程中处理handler
的操作,就要必须要自己生成Looper
了。
例2 、子线程中使用handler
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler=new Handler();
handler.post(new Runnable() {
@Override
public void run() {
}
});
Looper.loop();
}
});
上面在Thread
中使用handler
,先执行Looper.prepare
方法,来在当前线程中生成一个Looper
对象并保存在当前线程的ThreadLocal
中。 看下Looper.prepare()
中的源码:
//prepare
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
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到prepare
方法中会先从sThreadLocal
中取如果之前已经生成过Looper
就会报错,否则就会生成一个新的Looper
并且保存在线程的ThreadLocal
中,这样可以确保每一个线程中只能有一个唯一的Looper
。
另外:由于Looper
中拥有当前线程的引用,所以有时候可以用Looper
的这种特点来判断当前线程是不是主线程。
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
boolean isMainThread() {
return Objects.requireNonNull(Looper.myLooper()).getThread() ==
Looper.getMainLooper().getThread();
}
sendMessage vs post
先来看看sendMessage
的代码调用链:
enqueueMessage
源码如下:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage
的代码处理很简单,msg.target = this;
就是把当前的handler
对象给message.target
。然后再讲message
进入到队列中。
post代码调用链:
调用post
时候会先调用getPostMessage
生成一个Message
,后面和sendMessage
的流程一样。下面看下getPostMessage
方法的源码:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到getPostMessage
中会先生成一个Messgae
,并且把runnable
赋值给message
的callback.
消息都放到MessageQueue
中后,看下Looper
是如何处理的。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
}
Looper
中会遍历message
列表,当message
不为null
时调用msg.target.dispatchMessage(msg)
方法。看下message
结构:
也就是说msg.target.dispatchMessage
方法其实就是调用的Handler中的dispatchMessage
方法,下面看下dispatchMessage
方法的源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//
private static void handleCallback(Message message) {
message.callback.run();
}
因为调用post
方法时生成的message.callback=runnable
,所以dispatchMessage
方法中会直接调用 message.callback.run();
也就是说直接执行post
中的runnable
方法。 而sendMessage
中如果mCallback
不为null
就会调用mCallback.handleMessage(msg)
方法,否则会直接调用handleMessage
方法。
总结 post
方法和handleMessage
方法的不同在于,post
的runnable
会直接在callback
中调用run
方法执行,而sendMessage
方法要用户主动重写mCallback
或者handleMessage
方法来处理。
3、Looper会一直消耗系统资源吗?
首先给出结论,Looper
不会一直消耗系统资源,当Looper
的MessageQueue
中没有消息时,或者定时消息没到执行时间时,当前持有Looper
的线程就会进入阻塞状态。
下面看下looper
所在的线程是如何进入阻塞状态的。looper
阻塞肯定跟消息出队有关,因此看下消息出队的代码。
消息出队
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
}
}
上面的消息出队方法被简写了,主要看下面这段,没有消息的时候nextPollTimeoutMillis=-1
;
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
看for循环里面这个字段所其的作用:
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
Binder.flushPendingCommands();
这个方法的作用可以看源码里面给出的解释:
/**
* Flush any Binder commands pending in the current thread to the kernel
* driver. This can be
* useful to call before performing an operation that may block for a long
* time, to ensure that any pending object references have been released
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
也就是说在用户线程要进入阻塞之前跟内核线程发送消息,防止用户线程长时间的持有某个对象。再看看下面这个方法:nativePollOnce(ptr, nextPollTimeoutMillis);
当nextPollingTimeOutMillis=-1
时,这个native
方法会阻塞当前线程,线程阻塞后,等下次有消息入队才会重新进入可运行状态,所以Looper
并不会一直死循环消耗运行内存,对队列中的颜色消息还没到时间时也会阻塞当前线程,但是会有一个阻塞时间也就是nextPollingTimeOutMillis>0
的时间。
当消息队列中没有消息的时候looper肯定是被消息入队唤醒的。
消息入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/7ea7e9c1e1c3c168bdc1068dd474e526.png)
![img](https://img-blog.csdnimg.cn/img_convert/8a2352aa74fbc1ab8a87e032d7e07c70.png)
![img](https://img-blog.csdnimg.cn/img_convert/aa3fc4e2e76af4adf56ef180c047f4a1.png)
![img](https://img-blog.csdnimg.cn/img_convert/e06ec8d34bb5d70140541dcbd02d3552.png)
![img](https://img-blog.csdnimg.cn/img_convert/d61b17f2d3c8aafccfaa118493ecaa8d.png)
![img](https://img-blog.csdnimg.cn/img_convert/cd7beae7b3c22455e65cff7e4720ebfd.png)
![img](https://img-blog.csdnimg.cn/13f2cb2e05a14868a3f0fd6ac81d625c.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)**
![img](https://img-blog.csdnimg.cn/img_convert/e03b4237a8f48419897c785a75c0437c.png)
### 总结
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套**腾讯、字节跳动、阿里、百度2019-2021面试真题解析**,我把技术点整理成了**视频和PDF**(实际上比预期多花了不少精力),包**知识脉络 + 诸多细节**。
还有 **高级架构技术进阶脑图、Android开发面试专题资料** 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
![一线互联网面试专题](https://img-blog.csdnimg.cn/img_convert/6c28b8e909c9596e4df7826ed231473a.webp?x-oss-process=image/format,png)
![379页的Android进阶知识大全](https://img-blog.csdnimg.cn/img_convert/072351b91d715857c091d3862cbeef44.webp?x-oss-process=image/format,png)
![379页的Android进阶知识大全](https://img-blog.csdnimg.cn/img_convert/f703c6dff7e7e32155381db5271a2ed4.webp?x-oss-process=image/format,png)
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
> 2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
![img](https://img-blog.csdnimg.cn/img_convert/d7f4afae7a28ce85177df67cec81057e.png)
712835996365)]
[外链图片转存中...(img-P2OsrBlz-1712835996365)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
> 2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-PkwSrab2-1712835996365)]