【原创】源码角度分析Android的消息机制系列(六)——Handler的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载。
先看Handler的定义:
/**
* A Handler allows you to send and process {@link Message} and Runnable
* objects associated with a thread's {@link MessageQueue}. Each Handler
* instance is associated with a single thread and that thread's message
* queue. When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it -- from that point on,
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
*
* <p>There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
* ……….
* */
public class Handler {
……..
}
由源码中对Handler的定义以及注释,我们可知,Handler主要就是用来发送和处理消息的。每一个Handler的实例都和一个线程以及该线程的MessageQueue相关联。Hadnler主要有2个作用:①在未来某个时刻去发送或处理Message或Runnable(post方法)②在另一个线程中去处理消息(send方法)。
再看Handler的构造方法:
public Handler() {
this(null, false);
}
…….
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
……
由构造方法可知,当前线程中没有Looper时,若创建Handler对象,则会抛出"Can't create handler inside thread that has not called Looper.prepare()"异常。所以,必须要在有Looper的线程中创建Handler,否则,程序将抛出异常。
Handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post的一系列方法以及send的一系列方法来实现。下面来看一系列post方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
通过源码,我们可以知道,post的一系列方法最终还是通过send的一系列方法来实现的。
下面看send的一系列发送消息的源码:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
由源码可知,Handler发送消息的过程,其实就是向消息队列中插入了一条消息。
由MessageQueue的工作原理和Looper的工作原理我们可以知道,当MessageQueue中插入了新的消息后,next方法就会返回该消息给Looper,Looper接收到消息并开始处理消息,但最终Looper是通过调用Handler的dispatchMessage方法来处理消息的,即消息最终还是交给了Handler去处理。
下面来看Handler的dispatchMessage方法的源码,如下:
public void dispatchMessage(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();
}
首先,判断了Message的callback是否为null,若不为null,则调用handleCallback方法。
Message中有属性: /*package*/ Runnable callback; 那么,message.callback即Handler的post方法所传递的Runnable参数。再结合handleCallback方法的源码可知,handleCallback方法其实就是开启了一个子线程,去处理post方法。
再看dispatchMessage方法的源码,若Message的callback为空,则判断mCallback是否为null,由Handler的源码:
final Callback mCallback;
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
可知,Callback就是一个接口,而且其中定义了handleMessage方法。由此我们可以联想到,当需要获取一个Handler实例时,我们除了可以继承Handler,重写handleMessage方法外,我们还可以通过实现Callback 接口,然后实现接口中的handleMessage方法来实现。
接着来看dispatchMessage方法的源码,若mCallback为null,最后还是调用handleMessage方法来处理消息。
在开发过程中,当用Handler处理消息时,我们一般是需要重写handleMessage方法的,处理消息的逻辑由我们自己来写。
【原创】源码角度分析Android的消息机制系列(六)——Handler的工作原理的更多相关文章
- 【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻 ...
- 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...
- 【原创】源码角度分析Android的消息机制系列(二)——ThreadLocal的工作过程
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一篇文章中,我们已经提到了ThreadLocal,它并非线程,而是在线程中存储数据用的.数据存储以后,只能在指定的线程中获取到数据,对于其 ...
- 【原创】源码角度分析Android的消息机制系列(三)——ThreadLocal的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Android源码(API24)中对ThreadLocal的定义: public class ThreadLocal<T> 即 ...
- 【原创】源码角度分析Android的消息机制系列(四)——MessageQueue的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. MessageQueue,主要包含2个操作:插入和读取.读取操作会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和ne ...
- 源码角度分析-newFixedThreadPool线程池导致的内存飙升问题
前言 使用无界队列的线程池会导致内存飙升吗?面试官经常会问这个问题,本文将基于源码,去分析newFixedThreadPool线程池导致的内存飙升问题,希望能加深大家的理解. (想自学习编程的小伙伴请 ...
- Android的Message Pool是什么——源码角度分析
原文地址: http://blog.csdn.net/xplee0576/article/details/46875555 Android中,我们在线程之间通信传递通常采用Android的消息机制,而 ...
- 【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行
壹 ❀ 引 我在[react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?一文中,介绍了虚拟dom的概念,以及react中虚拟dom的使用场景 ...
- 从源码角度理解android动画Interpolator类的使用
做过android动画的人对Interpolator应该不会陌生,这个类主要是用来控制android动画的执行速率,一般情况下,如果我们不设置,动画都不是匀速执行的,系统默认是先加速后减速这样一种动画 ...
随机推荐
- iOS系统原生 二维码的生成、扫描和读取(高清、彩色)
由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上.连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付.可见现在的二维 ...
- 连续分段累计器FPGA实现的探讨
- ASP.NET MVC Filter的思考
思考了一下AOP的具体实现,后来想到ASP.NET MVC过滤器其实就是AOP的一种,于是从Filter下手研究AOP. 暂时先考虑AuthorizationFilter,ActionFilter,R ...
- 项目管理之 Git 管理软件 SourceTree for Mac
Git 项目管理: Mac Terminal 生成 Git 秘钥流程: git config --global user.name "yourname" git config -- ...
- kafka 0.10.2 部署失败后,重新部署
删除kafka各个节点log目录 删除zookeeper上kafka相关的目录 [root@m1 ~]# zkCli.sh Connecting to localhost: -- ::, [myid: ...
- 多线程异步编程示例和实践-Thread和ThreadPool
说到多线程异步编程,总会说起Thread.ThreadPool.Task.TPL这一系列的技术.总结整理了一版编程示例和实践,分享给大家. 先从Thread和ThreadPool说起: 1. 创建并启 ...
- 2017年Web前端开发工程师薪资越来越高?
放眼全球,不仅在国内的互联网行业,在国外,前端工程师一样是需求旺盛.供不应求的香饽饽.所以在供不应求的前端招聘市场上,优秀的前端工程师才是有话语权的那一方. 前端开发是做什么的? 前端是互联网时代软件 ...
- Java经典编程题50道之五十
有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成绩),计算出平均成绩,况原有的数据和计算出的平均分数存放在磁盘文件 "stud"中. public ...
- python通过excel对数据库插入数据
1.需要有两个包文件xlrd及MySQLdb(其他数据库可以另外找) 2.读取excel文件信息 book = xlrd.open_workbook(文件地址) 3.建立MySQL链接 databas ...
- js禁止浏览器的回退事件
直接来个终极方案: 查找了好多资料才找到的,这种方式,可以消除 后退的所有动作.包括 键盘.鼠标手势等产生的后退动作. <script language="javascript&quo ...