Handle的原理(Looper、Handler、Message三者关系)
转载请注明出处:http://blog.csdn.net/lowprofile_coding/article/details/72580044
介绍
前面的内容对Handler做了介绍,也讲解了如何使用handler,但是我们并不知道他的实现原理。本文从源码的角度来分析如何实现的。
首先我们得知道Handler,Looper,Message Queue三者之间的关系
- Handler封装了消息的发送,也负责接收消。内部会跟Looper关联。
- Looper 消息封装的载,内部包含了MessageQueue,负责从MessageQueue取出消息,然后交给Handler处理
- MessageQueue 就是一个消息队列,负责存储消息,有消息过来就存储起来,Looper会循环的从MessageQueue读取消息。
源码分析
当我们new一个Handler对象的时候,看看他的构造方法里面做了什么.
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.myLooper方法获取一个Looper对象,然后从Looper对象获取到MessageQueue对象。
Looper myLooper()
跟进去看看Looper.myLooper()方法做了什么。这是一个静态方法,可以类名.方法名直接调用。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这个方法里面就一行代码,从sThreadLocal中获取一个Looper对象,sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。底层是ThreadLocalMap,既然是Map类型那肯定得先set一个Looper对象,然后我们才能从sThreadLocal对象里面get一个Looper对象。
ActivityThread main()
说到这里得给大家介绍一个新的类ActivityThread,ActivityThread类是Android APP进程的初始类,它的main函数是这个APP进程的入口。我们看看这个main函数干了什么事情。
public static final void main(String[] args) {
------
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
-----
}
在第二行代码调用Looper.prepareMainLooper()方法,第13行调用了Looper.loop()方法。
Looper prepareMainLooper()
继续跟进Looper.prepareMainLooper()方法,在这个方法中第一行代码调用了内部的prepare方法。prepareMainLooper有点像单例模式中的getInstance方法,只不过getInstance会当时返回一个对象,而prepareMainLooper会新建一个Looper对象,存储在sThreadLocal中。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Looper prepare()
继续跟进prepare方法,看第5行代码,新建了一个Looper对象,调用sThreadLocal.set方法把Looper对象保存起来。看到这里我想聪明的你们肯定明白了为什么new Handler对象的时候调用Looper.myLooper()方法能从sThreadLocal对象中取到Looper对象。
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 构造方法
文章开头我们就讲到Looper内部包含了MessageQueue,其实就是在new Looper对象的时候就new了一个MessageQueue对象。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper loop()
ActivityThread类main方法中调用了Looper的两个方法,前面我们解释了prepareMainLooper(),现在来看第二个方法loop()。
public static void loop() {
final Looper me = myLooper();//获取Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//从Looper对象获取MessageQueue对象
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {//死循环 一直从MessageQueue中遍历消息
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//调用handler的dispatchMessage方法,把消息交给handler处理
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
这个方法的代码呢比较多。我都给代码加上了注释。其实就是一个死循环,一直会从MessageQueue中取消息,如果取到了消息呢,会执行msg.target.dispatchMessage(msg)这行代码,msg.target就是handler,其实就是调用handler的dispatchMessage方法,然后把从MessageQueue中取到的message传入进去。
Handler dispatchMessage()
public void dispatchMessage(Message msg) {
//如果callback不为空,说明发送消息的时候是post一个Runnable对象
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//这个是用来拦截消息的
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最终调用我们重写的handleMessage方法
}
}
这个方法对消息做最后处理,如果是post类型调用handleCallback方法处理,如果是sendMessage发送的消息。看我们有没有拦截消息,如果没有最终调用handleMessage方法处理。
Handler handleCallback()
看到这里我们知道为什么post一个Runnable对象,run方法执行的代码在主线程了吧,因为底层根本就没有开启线程,就只是调用了run方法而已。
private static void handleCallback(Message message) {
message.callback.run();
}
前面我们从创建handler对象开始,以及创建Looper,创建MessageQueue的整个流程,现在来分析下,当我们调用post以及sendMessage方法时,怎么把Message添加到MessageQueue。
Handler post()
调用了getPostMessage方法,把Runnable传递进去。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
Handler getPostMessage()
首先调用Message.obtain()方法,取出一个Message对象,这个方法之前有讲过,然后把Runnable对象赋值了Message对象的callback属性。看到这里我们也明白了dispatchMessage方法为什么要先判断callback是否为空了吧。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Handler enqueueMessage()
在post方法里面调用了sendMessageDelayed方法,其实最终调用的是enqueueMessage方法,所以我这里就直接看enqueueMessage方法源码。第一行代码就把handler自己赋值给messgae对象的target属性。然后调用MessageQueue的enqueueMessage方法把当前的Messgae添加进去。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
总结
总结:handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己。MessageQueue就是一个存储消息的容器。
如果你想第一时间看我的后期文章,扫码关注公众号,每周不定期推送Android开发实战教程文章...
Android开发666 - 安卓开发技术分享
扫描二维码加关注
Handle的原理(Looper、Handler、Message三者关系)的更多相关文章
- 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
转自:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的 ...
- Android的消息处理机制Looper,Handler,Message
android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...
- android的消息处理机制——Looper,Handler,Message
在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将 ...
- 转 Android的消息处理机制(图+源码分析)——Looper,Handler,Message
作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种 ...
- 【转】android的消息处理机制(图+源码分析)——Looper,Handler,Message
原文地址:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html#!comments 作为一个大三的预备程序员,我学习 ...
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android ...
- android的消息处理机制(图文+源码分析)—Looper/Handler/Message[转]
from:http://www.jb51.net/article/33514.htm 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.andro ...
- Android -- 消息处理机制源码分析(Looper,Handler,Message)
android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...
随机推荐
- ubuntu root默认密码(初始密码)
ubuntu安装好后,root初始密码(默认密码)不知道,需要设置. 1.先用安装时候的用户登录进入系统 2.输入:sudo passwd 按回车 3.输入新密码,重复输入密码,最后提示passwd ...
- Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试
Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试 会撸码的小马 关注 2018.05.29 17:30* 字数 212 阅读 1488评论 0喜欢 2 接到上一章, ...
- python之PIL模块基础功能
Image主要是打开图片后,对图片进行编辑,主要有以下一些常用功能: 1.读取并显示图片: from PIL import Image img = Image.open("H:\\salar ...
- 用Eclipse导入Maven工程
步骤一 : 选择 “Import”操作 有两个途径可以选择 “Import”操作; 1>“File”--> "Import..." 2> 在 "Pro ...
- pycharm中可以运行脚本但是不能debug脚本
以前用的时候好好地,但是最近上班突然脚本就不能debug了,debug直接报错,如下所示 上网查过该有的原因: 1.在pycharm中两个地方设置成utf-8,页面右下角和File>settin ...
- Python:SQLMap源码精读—基于错误的盲注(error-based blind)
目标网址 http://127.0.0.1/shentou/sqli-labs-master/Less-5/?id=1 Payload的生成 <test> <title>MyS ...
- Windows下memcache的配置和使用(python)
1.memcache的安装和配置: 下载memcache for windows: http://up.2cto.com/2012/0522/20120522094758371.rar 解压到指定目 ...
- Apache Commons Digester 三(规则注解)
前言 Digester规则的定义除了可以在代码中直接new规则添加到 Digester对象外,还可以用xml配置规则,如下所示: <digester-rules> <pattern ...
- iOS ipa包瘦身,iOS8及以下text段超60MB
前沿 很早之前写过一篇相关文章,不过博客主机上跑路了之后数据没了,凭着记忆补了下相关资料 ipa安装包瘦身 清理无用图片,图片压缩(PNG换WebP和JPG),处于某种不可抗拒的原因,导致有部分3X图 ...
- 深度学习论文翻译解析(三):Detecting Text in Natural Image with Connectionist Text Proposal Network
论文标题:Detecting Text in Natural Image with Connectionist Text Proposal Network 论文作者:Zhi Tian , Weilin ...