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打交道,因 ...
随机推荐
- Shell-13--while和until
- Vue-Cli 搭建项目 小白
vue-用Vue-cli从零开始搭建一个Vue项目 Vue是近两年来比较火的一个前端框架(渐进式框架吧). Vue两大核心思想:组件化和数据驱动.组件化就是将一个整体合理拆分为一个一个小块(组件),组 ...
- div、span内容超出省略号
span设置部分省略...: span{ overflow: hidden; text-overflow: ellipsis; -o-text-over ...
- org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException
相信很多的朋友在开发过程中都或多或少的遇见一些异常,下面我给大家说一说NoSuchRequestHandlingMethodException 这个异常说的是找不到处理这样的请求方法,那是什么原因导致 ...
- tomcat-四种运行模式和三种部署模式(优化)
四中运行模式如下: 1-bio: 传统的Java I/O操作,同步且阻塞IO. 2-nio: JDK1.4开始支持,同步阻塞或同步非阻塞IO 3-aio(nio.2): JDK7开始支持,异步非阻塞I ...
- 1ink 与 @import 的区别
1ink与@import的区别 目录 1ink与@import的区别 差别1:归属关系的差别 差别2:加载顺序的差别 差别3:兼容性的差别 差别4:使用dom控制样式时的差别 1ink与@import ...
- OpenMP初探
OpenMP支持c.cpp.fortran,本文对比使用openmp和未使用openmp的效率差距和外在表现,然后讲解基础知识. 一.举例 1.使用OpenMP与未使用OpenMP的比较. OpenM ...
- 通过清华大学镜像下载Android源码并编译源码
之前看源码都是在Windows下用SourceInsight看,虽然达到了研究源码的效果,但终究还是有遗憾...趁着周末,准备在Ubuntu虚拟机上下载编译源码. 之前下源码时,有了解一些Androi ...
- TCP/IP 笔记 - 用户数据报协议和IP分片
关于本章中的IP分片部分,参考第五章IP分片头部知识点.需要注意的是,TCP有超时重传,UDP的超时重传则依赖上层应用程序实现. 用户数据报协议(UDP) UDP是一个简单的面向无连接.不可靠的数据报 ...
- PHP中文转拼音函数
<?php function Pinyin($_String, $_Code='UTF8'){ //GBK页面可改为gb2312,其他随意填写为UTF8 $_DataKey = "a| ...