【原创】源码角度分析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动画的执行速率,一般情况下,如果我们不设置,动画都不是匀速执行的,系统默认是先加速后减速这样一种动画 ...
随机推荐
- ASP.NET MVC5路由系统机制详细讲解
请求一个ASP.NET mvc的网站和以前的web form是有区别的,ASP.NET MVC框架内部给我们提供了路由机制,当IIS接受到一个请求时,会先看是否请求了一个静态资源(.html,css, ...
- js导出CSV
<html> <head> <meta http-equiv="content-type" content="text/html; char ...
- 关于Java解压文件的一些坑及经验分享(MALFORMED异常)
文章也已经同步到我的csdn博客: http://blog.csdn.net/u012881584/article/details/72615481 关于Java解压文件的一些坑及经验分享 就在本周, ...
- python 计算两个日期相差多少个月
近期,由于业务需要计算两个日期之前相差多少个月.我在网上找了很久,结果发现万能的python,居然没有一个模块计算两个日期的月数,像Java.C#之类的高级语言,都会有(date1-date2).mo ...
- 基于TypeScript的FineUIMvc组件式开发(概述)
WebForm与Mvc 我简单说一下WebForm与Mvc,WebForm是微软很早就推出的一种WEB开发架构,微软对其进行了大量的封装,使开发人员可以像开发桌面程序一样去开发WEB程序,虽然开发效率 ...
- Hibernate SQLQuery 原生SQL 查询及返回结果集处理-1
第一篇:官方文档的处理方法,摘自官方 在迁移原先用JDBC/SQL实现的系统,难免需要采用hibernat native sql支持. 1.使用SQLQuery hibernate对原生SQL查询执行 ...
- SpringMVC中使用RedirectAttributes重定向传参,防止暴露参数
RedirectAttributes是SpringMVC3.1版本之后出来的一个功能,专门用于重定向之后还能带参数跳转的. 当我从jsp页面函数中带参数到controller层方法,方法执行完毕后返回 ...
- Linux_破解密码-营救模式
实验用机:CentOS 5.7 破解密码 设置开机启动界面 系统运行级别 营救模式 一.破解密码 root用户可以更改任何用户的密码,普通用户只能修改自己的密码. 步骤: 1.重新启动系统 2.开机倒 ...
- CMT2300 收发一体 SUB 1G 支持灵活选频
CMT2300A 是一款超低功耗,高性能,适用于各种140 至1020 MHz 无线应用的OOK,(G)FSK 射频收发器.它是CMOSTEK NextGenRFTM 射频产品线的一部分,这条产品线包 ...
- 分布式版本控制git常见问题之gitignore冲突(精简版)
上次写的的太模糊了,现在简单直接写出个人心得,如下: 原因是有人提交了.gitignore里面的内容,所以和本地的不一样,这样就有问题,那么pull都不可以,所以要这样: git update-ind ...