一、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/

最新文章

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

热门文章

  1. Hibernate映射多对多双向关联关系(小案例)
  2. butterknife简介及Generate ButterKnife Injections 不出现的问题解决
  3. 高德地图 室内地图 API 的一些坑
  4. Eclipse安装jad插件进行反编译
  5. 数据库---MySQL练习题及答案
  6. IOS第13天(2,私人通讯录,plist存储,偏好设置,归档)
  7. poj3292-Semi-prime H-numbers(筛法打表)
  8. javaweb--HTTP状态码
  9. hover事件优化(延时操作)
  10. jquery选择伪元素属性的方法