一、Android应用程序的主线程主要用于更新UI界面,并且主线程不能做耗时操作,否则会引起ANR;这种情况下需要开一个子线程来进行耗时操作,动作完成之后,子线程发消息给主线程通知其更新UI显示,常见方法有:
- Activity.runOnUiThread(Runnable);
- View.post(Runnable);
- View.postDelayed(Runnable, long);
- Handler消息机制。
注:经过看源码,会发现Activity.runOnUiThread(Runnable),View.post(Runnable),View.postDelayed(Runnable, long)最终本质上会调用到Handler发送消息的方法,如下代码:
Activity.runOnUiThread(Runnable);
/**
* Runs the specified action on the UI thread. If the current thread is the UI
* thread, then the action is executed immediately. If the current thread is
* not the UI thread, the action is posted to the event queue of the UI thread.
*
* @param action the action to run on the UI thread
*/
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
View.Post(Runnable),View.PostDelayed(Runnable); 请忽略attachInfo判断。
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
} public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}
二、弄清消息机制之前,提一个问题:如何在子线程实例化一个Handler发送message呢?代码很简单,如下
new Thread(){ @Override
public void run() {
super.run();
Looper.prepare();
new Handler().sendEmptyMessage(1);
Looper.loop();
} }.start();
接下来,问题又来了:
1.发消息的时候为什么要调用Looper.prepare();
2.消息发送后为什么要调用Looper.loop();
3.为什么在主线程里发送信息,我们没有调用 这两个方法呢?
在解答这三个问题之前,先看一张图,大致了解一下消息机制是如何运行的?如下图:
总结一下上面那个图片:在UI线程中有一个消息队列MessageQueue,其它线程do something之后,在UI线程中的消息队列MessageQueue插入Message,而Looper负责轮循消息队列MessageQueue。然后来回答上面第1个问题,看代码:
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));
} //初始化Looper,并且初始化消息队列
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通过上图可得知,接收一个消息的前提是,该线程得拥有自己的消息队列MessageQueue,而第1个问题的答案就是创建一个该线程接收消息的一个消息队列。然后第二个问题请看以下代码:
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 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); 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,还得有轮循,第二个问题就是执行轮循的操作。第三个问题请往下看代码:
public final class ActivityThread {
...... public static final void main(String[] args) {
......
//将当前线程初始化为Looper线程。最终会调用Looper.prepare()
Looper.prepareMainLooper(); ......
// 开始循环处理消息队列
Looper.loop(); ......
}
}
第三个问题的答案:android应用程序启动过程中,会在进程中执行ActivityThread中main方法,来初始化该应用中UI线程的Looper。这也就是为什么主线程里可以直接调用Handler,而子线程不能直接调用Handler发送消息。
参考:http://3dobe.com/archives/74/
android 消息机制的更多相关文章
-
Android消息机制
每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时 ...
-
Android消息机制:Looper,MessageQueue,Message与handler
Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...
-
Android消息机制不完全解析(上)
Handler和Message是Android开发者常用的两个API,我一直对于它的内部实现比较好奇,所以用空闲的时间,阅读了一下他们的源码. 相关的Java Class: androi ...
-
Android消息机制不完全解析(下)
接着上一篇文章Android消息机制不完全解析(上),接着看C++部分的实现. 首先,看看在/frameworks/base/core/jni/android_os_MessageQueue.cpp文 ...
-
Android 消息机制 (Handler、Message、Looper)
综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...
-
Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
-
【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制
本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top2 Crash发生场景:在很平常.频繁的使用页面,打开一个界面,马 ...
-
Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
-
android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
-
Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...
随机推荐
-
算法笔记_013:汉诺塔问题(Java递归法和非递归法)
目录 1 问题描述 2 解决方案 2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...
-
Vue2随笔
1. computed有缓存 methods没有缓存 慢慢更新中..... 2.Class与Style绑定 <div class="static" v-bind:class= ...
-
Titon Toolkit – 非常强大的用户界面组件
Titon Toolkit 是一个非常强大的用户界面组件,也是实现响应式,移动和现代网页的工具类的集合.每个组件封装了 HTML.CSS 以及为角色特定页面元素的 JavaScript 功能.Tool ...
-
ubuntu中jdk已经安装,但是eclipse启动报错
问题描述 在ubuntu中,jdk已经正常安装,java_home变量已经配置,但是启动eclipse的时候还是弹出以下错误信息: A Java RunTime Environment (JRE) o ...
-
I.MX6 eMMC 添加分区
/********************************************************************************* * I.MX6 eMMC 添加分区 ...
-
du 命令
Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...
-
自动、手动同步FishEye, JIRA的联系人信息
背景:在将FishEye和JIRA配置成共用用户信息,并且可以在二者之间*切换,此时FishEye里的用户信息是不能更改的,只有更新了JIRA,然后让其同步至FishEye才行,如何进行设置呢? 答 ...
-
C++中不常用关键字
mutable关键字 关键字mutable是C++中一个不常用的关键字,他只能用于类的非静态和非常量数据成员.我们知道一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,对像的状态也会 ...
-
maven工程-eclipse红叹号
从公司代码库中check下一份完整代码,在其他开发机器上都能正确构建,唯独一台机器无法正确构建,查证了2个小时,最后还是死在了最基础的问题上 maven工程 工程如下,创建了一个新的maven工程,j ...
-
在使用EF时,想要比较字符串类型的日期时
原文地址(https://blog.csdn.net/yangxinyue315/article/details/44960895) 在使用EF时,想要比较字符串类型的日期时 在使用EF时,想要比较字 ...