handler 源代码分析
handler
Looper 轮询器
MessageQueue 消息对象
1 主线程在一创建的时候就会调用, public static void prepareMainLooper() {}构造方法。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
2 在prepareMainLooper(){} 内部调用了 prepare(false);方法,这就是在子线程中new Handler()会抱错的关键
prepare(quitAllowed) {}方法里面设置了一个Looper对象。假设已经有了 Looper 对象,会抛出异常 Only one Looper may be created per thread
所以说一个 Handler仅仅能有一个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构造器
}
3 在 Looper 的构造器中
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了 MessageQueue对象
mRun = true;
mThread = Thread.currentThread();//线程对象
}
4 可是在 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;
}
5 查看 Looper.myLooper();
public static Looper myLooper() {
return sThreadLocal.get();//返回的是一个 Looper对象。这里就跟 2的结果一样了
}
所以在4 中抛出异常。跟2 也一样了
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
所以敢肯定(1 2 3)的原理就是主线程 Handler的工作原理
而 (4 5)就是我们手动创建 Handler的时候的工作原理。
handler.sendMessage(msg);他做的是将消息入队操作
6 经过源代码跟踪。会发如今调用enqueueMessage(){}构造方法的时候。所做的事情就是将消息即可。入栈处理
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//注意这里。想必从字面意思理解,enqueueMessage就是入栈的意思吧
}
7 看enqueueMessage()所做的事情
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg; //将消息对象的引用赋值给 Message
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()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg; //将消息对象的引用赋值给 Message
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
8 那么问题来了。消息引用都传递给Message对象了。那是怎样从 Message中吧消息分发出去。并响应呢?这就得看 Looper的源代码中的 public static void loop() {}方法
事实上 loop就是一个轮询器,在不断的从 MessageQueue中获取消息,能够看 loop()中的 Message msg = queue.next(); 内部实现源代码,next() 方法就是消息队列的出队方法。
只是因为这种方法的代码略微有点长,我就不贴出来了,它的简单逻辑就是假设当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages。否则就进入一个堵塞状态,一直等到有新的消息入队
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 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 (;;) {//天呐,在这里竟然是 for的空循环
//queue.next() 出现了,有兴趣的能够点进去看看
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//我们又发现了什么?对。msg.target代表的是Handler,调用了dispatchMessage方法
// 这样我相信大家就都明确了为什么handleMessage()方法中能够获取到之前发送的消息了吧!
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.recycle();
}
}
handler 源代码分析的更多相关文章
- Android之Handler源代码深入解析
闲着没事.就来看看源代码,看看源代码的各种原理,会用仅仅是简单的,知道为什么才是最牛逼的. Handler源代码分析那,从使用的步骤来边用边分析: 1.创建一个Handler对象:new Handle ...
- hostapd源代码分析(二):hostapd的工作机制
[转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...
- Hadoop源代码分析
http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...
- Android应用程序消息处理机制(Looper、Handler)分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6817933 Android应用程序是通过消息来 ...
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...
- Android应用程序进程启动过程的源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址: http://blog.csdn.net/luoshengyang/article/details/6747696 Android 应用程序框架层创 ...
- Android应用程序绑定服务(bindService)的过程源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6745181 Android应用程序组件Serv ...
- Android 消息处理源代码分析(1)
Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...
- Memcached源代码分析 - Memcached源代码分析之消息回应(3)
文章列表: <Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)> <Memcached源代码分析 - Memcached源代码分析 ...
随机推荐
- 利用JavaScript实现动态显示表格且对应改变按键的value值
插入的代码并没有符合HTML5样式,只是为了实现利用JS动态显示表格,并且按键的value值会同时发生变化的功能. <!DOCTYPE > <html > <head&g ...
- 基于webpack搭建的vue+element-ui框架
花了1天多的时间, 终于把这个框架搭建起来了. 好了, 不多说了, 直接进入主题了.前提是安装了nodejs,至于怎么安装, 网上都有教程. 这里就不多说了, 这边使用的IDE是idea.1.在E:/ ...
- 开发环境MAPLAB下使用仿真器ICD2程序下载流程
程序下载流程 一. 连接示意图 二. 下载步骤 1.目标板电源断开 2.将仿真器下载端口与电路板JTAG端口有效连接 3.启动MPLAB软件 4.点击MAPLAB软件上方Programme ...
- 大话git中的撤销操作
下面以现实场景作为情境. 基础知识,理解git中的几个区域 本地代码已经add,未commit 修改本地工作目录中的readme.md,添加文字"第一次修改" 然后查看下状态 ➜ ...
- asp.net web api 文件上传
1正确的做法 public class AvaterController : BaseApiController { [HttpPost] public async Task<IHttpActi ...
- C语言一些知识点回顾
1.多行宏定义 #include <stdio.h> #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _ ...
- java-基础-泛型
java泛型通配符问题. java中的泛型基本用法参考<java编程思想>第四版 p.353 java泛型中比较难理解的主要是类型擦除和通配符相关. 1.类型擦除 在编译期间,类型 ...
- java面向对象整理
1.局部变量与全局变量的区别 区别一:定义的位置不同 定义在类中的变量是成员变量 定义在方法中或者{}语句里面的变量是局部变量定义 区别二:在内存中的位置不同 成员变量存储在对内存的对象中 局部变量存 ...
- SQL Server 基本操作之三种增加法
前言: 数据库操作避免不了对数据的操作,操作方法大同小异,万变不离其宗,今就写一下各种花式操作的根本增删改查四种操作,今天我们就来说一下增加操作的三种方法 正文: 增加操作是对数据库进行数据行的添加, ...
- while循环小练习-猜答案
条件 1.每个用户只能猜10次产品的价格2.每次猜玩价格,提示用户价格是多了还是少了或者对了3.如果用户才对则终止程序执行 break! i = 0 #设置一个次数变量 price = 38 #设置一 ...