android 消息机制
一、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/
最新文章
- mysql-5.7.11-winx64.zip 安装配置
- Android笔记——Android中visibility属性VISIBLE、INVISIBLE、GONE的区别
- Python格式化字符串~转
- kylin(二): Calcite
- flashftp连接虚拟机centos报错的解决方法
- spring配置的相关文章
- HTTP断点续传的基本原理
- 使用javaScript解决asp.net中mvc使用ajax提交数组参数的匹配问题
- JQuery事件处理的注意事项
- attribute section的用法
- CGRectInset CGRectoffset UIEdgeInsetsInsetRect 这三个函数的使用情况
- hibernate 延长加载范围 4.2
- Can you answer these queries?
- shell之ulimit应该注意的事项
- [LeetCode&;Python] Problem 506. Relative Ranks
- Linux内核分析(第五周)
- Android沉浸式状态栏兼容4.4手机的实现
- java的两种冒泡算法
- [Python] 网络编程之TCP编程
- iOS设计模式 - 生成器