解析Hander消息处理机制
本篇主要讲述Hander源码解析
首先我们new Hander点进去看看它的构造方法,
/*** Default constructor associates this handler with the {@link Looper} for the* current thread.** If this thread does not have a looper, this handler won't be able to receive messages* so an exception is thrown.*/public Handler() {this(null, false);}
调用了两个参数的,我们在往下看:
/*** Use the {@link Looper} for the current thread with the specified callback interface* and set whether the handler should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with respect to synchronous messages. Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.** @param callback The callback interface in which to handle messages, or null.* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/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对象
mQueue = mLooper.mQueue; 得到的是一个消息队列
mCallback = callback;是消息处理了一个回调
mAsynchronous = async;设置消息是否是异步的
/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}
以上就是Hander了构造方法了;
我们在看下这个异常,当mLooper==null的时候就会抛出下面这段话,
if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}
而mLooper又是通过 sThreadLocal.get()这个方法返回的;
那么这个sThreadLocal.get()是在哪里赋值的呢?
根据异常信息我们可以看到"无法在未调用Looper.prepare()的线程中创建处理程序"
大白话就是没有调用Looper.prepare()这个方法, 接下来我们就看看这个方法
/** Initialize the current thread as a looper.* This gives you a chance to create handlers that then reference* this looper, before actually starting the loop. Be sure to call* {@link #loop()} after calling this method, and end it by calling* {@link #quit()}.*/public static void prepare() {prepare(true);}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));}
走源码我们可以看出来 sThreadLocal.set(new Looper(quiAllowed));
所以我们用Hander之前要调用一下Looper.prepare()这个方法,接下肯定会有人疑惑为什么在主线程的没调用这个方法也可以直接使用Hander呀!!! 不急, 我们在看下ActivityThread中的main()方法:
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Process.setArgV0("<pre-initialized>"); 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(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
}
我们可以看到系统调用了一个Looper.prepareMainLooper()方法,我们在点进去看一下:
public static final void prepareMainLooper() {prepare();setMainLooper(myLooper());if (Process.supportsProcesses()) {myLooper().mQueue.mQuitAllowed = false;}
}
我们之所以能够在主线程中直接使用Hander原来系统已经默认帮我们调用prepare()这个方法,因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。而子线程中我们要使用Hander就必须自己手动调用一下Looper.prepare()方法了,
当Looper.prepare()走完以后接下来就会走Looper.loop()这个方法,我们在看下:
Looper.loop()的源码
/*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/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 (;;) {Message msg = queue.next(); // might blockif (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 loggerfinal 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.traceBegin(traceTag, msg.target.getTraceName(msg));}try {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();}}
上面的源码只要是就说明
先通过 final MessageQueue queue = me.mQueue; 得到一个消息队列
下面在一个for(;;)的死循环,
在通过queue.next();这个方法不停走消息队列里面获取消息
msg.target.dispatchMessage(msg); 这个方法将消息发送出去 msg.target这个就是一个Hander
/*** Handle system messages here.*/public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
当callback不为null的时候就调用handleCallback()这个方法,
private static void handleCallback(Message message) {message.callback.run();}
这方法就直接调用了一个run()方法,看到这里估计小伙伴们会有很多疑问了? 不要急 我们在看看Hander的post的方法
hander.post();
/*** Causes the Runnable r to be added to the message queue.* The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed.* * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the* looper processing the message queue is exiting.*/public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);}
这是一个sendMessage,我们看看getPostMessage(r)
private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}
原来callback就是Runnable,那么前面调用run()也就行的通了; 这就是线程的回调;
接下来我们在看看hander.sendMessage(msg);
public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}
跟post一样都是调用sendMessageDelayed(msg,0);
这里面第二参数就是该消息多少时间后发送默认是0;
/*** Enqueue a message into the message queue after all pending messages* before (current time + delayMillis). You will receive it in* {@link #handleMessage}, in the thread attached to this handler.* * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the* looper processing the message queue is exiting. Note that a* result of true does not mean the message will be processed -- if* the looper is quit before the delivery time of the message* occurs then the message will be dropped.*/public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
/*** Enqueue a message into the message queue after all pending messages* before the absolute time (in milliseconds) <var>uptimeMillis</var>.* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>* Time spent in deep sleep will add an additional delay to execution.* You will receive it in {@link #handleMessage}, in the thread attached* to this handler.* * @param uptimeMillis The absolute time at which the message should be* delivered, using the* {@link android.os.SystemClock#uptimeMillis} time-base.* * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the* looper processing the message queue is exiting. Note that a* result of true does not mean the message will be processed -- if* the looper is quit before the delivery time of the message* occurs then the message will be dropped.*/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);}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
注意这个事件
msg.target = this;
这就是我上面为什么会说 msg.target就是一个hander了
在看看这个事件
queue.enqueueMessage
这个方法返回true就是说明消息已经保存到消息队列中了,反之失败;
以上就是hander发送消息的流程了;
总结: 只要是在子线程中使用Hander就先手动创建一个Looper.prepare(),
Hander在new出来的时候就在它的构造方法中通过sThreadLocal.get(),获取当前线程的Looper对象然后Looper有在自己的构造方法中创建MessageQueue消息队列,在调用Looper.loop开启死循环不停的走消息队列中获取消息,剩下就是hander.sendMessage()了;
解析Hander消息处理机制相关推荐
- Hander消息处理机制的步骤
### 使用handler更新UI步骤 1. 创建消息处理器 //1. 定义消息处理器 Handler handler = new Handler(){ public void hand ...
- Android异步消息处理机制 全解析
Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程. 本文将通过分析源码(api-28)的形式,全面解析Handl ...
- 简单解析android Hander处理机制
在没在官网上看到 frameWork 是 真的不知道 framework是什么 后来发现 就是在学的一下 hander binder 闹钟 通知 还有一下系统的服务 的东西 这里binder还没 ...
- 覆盖式理解Android 消息处理机制(带源码解析)
转载自:https://www.jianshu.com/p/02962454adf7 Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,A ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片载入类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...
- Android多线程----异步消息处理机制之Handler
虽然是国庆佳节,但也不能停止学习的脚步,我选择在教研室为祖国母亲默默地庆生. 关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在 ...
- Android之多线程----异步消息处理机制之Handler详解
一.handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟 ...
- Android线程之异步消息处理机制(二)——Message、Handler、MessageQueue和Looper
异步消息处理机制解析 Android中的异步消息处理主要有四个部分组成,Message.Handler.MessageQueue和Looper. 1.Message Message是在线程之间传递的消 ...
- Android开发之异步消息处理机制AsyncTask
转自:Android AsyncTask完全解析,带你从源码的角度彻底理解 另外一篇比较详细的博文:http://blog.csdn.net/liuhe688/article/details/6532 ...
最新文章
- Python实现阿里云aliyun服务器里的文件上传与下载
- 死里逃生!国足2:1逆转泰国队晋级亚洲杯八强
- Statement对象重新执行将关闭当前ResultSet
- STM32 UART2程序--端口重映射
- 1151压力变送器型号_日本进口横河EJA530E压力变送器型号解读
- HttpDNS与传统DNS的区别
- HALCON示例程序inspect_bottle_mouth.hdev玻璃瓶口缺陷检测
- expected initializer before
- C#多线程编程系列(五)- C# ConcurrentBag的实现原理
- 洛谷入门题P1008、P1035、P1423、P1424、P1980题解(Java语言描述)
- Qt学习笔记-Qt中OpenGL的使用
- 【JAVA】Maven profiles控制多环境数据源日志打包(转载)
- Windows Mobile开发的一些小技巧(持续更新)
- 如何用编程方式实现创建一个页面并替换掉站点首页
- Linux top命令里面%CPU和cpu(s)的差别
- 遗传算法之: One Max Problem
- Formtec.NCspeed.v5.1.0.4
- PASCAL VOC 2012
- 从零开始学USB(三、基础知识3)
- 全面剖析雅虎助手以及网络实名的流氓行径(1)
热门文章
- 【Linux-SVN】安装 SVN Server
- 通俗易懂数仓建模:范式建模与维度建模
- matlab体素化,教程|如何使用Matlab制造GrabCAD体素打印切片
- LTE Phich 分析
- 罗克韦尔自动化收购MESTECH Services
- 正确使用uniapp搭配微信开发者工具自带的骨架屏功能,生成骨架屏
- springmvc对json数据的处理
- jvm性能分析工具之-- Eclipse Memory Analyzer tool(MAT)
- modprobe命令介绍
- icpc2018-焦作-E Resistors in Parallel-数论+大数