Android开发——Android的消息机制详解

原创 2016年08月29日 22:57:41
  1. 我们为什么需要Android的消息机制
    我们知道,Android规定访问UI只能在主线程中进行。若在子线程中访问UI,就会抛出异常。这个验证由ViewRootImpl的checkThread方法来完成。
    为什么不允许在非主线程访问UI呢,这是因为Android的UI控件不是线程安全的。并发访问会导致控件处于不可预期的状态。
    那为什么不对UI访问加上锁机制呢,原因如下:
    (1)这显然会让UI访问的逻辑变得极其复杂;
    (2)除了效率问题,锁机制还会阻塞某些进程的执行。

但是Android又不建议在主线程进行耗时操作,因为这可能会引起ANR。因此,便出现了Android的消息机制。

  1. Android的消息机制结构
    Android的消息机制主要是指Handler的运行机制。Handler的运行需要底层MessageQueue和Looper的支撑。

2.1 MessageQueue
MessageQueue采用单链表的数据结构存储消息列表。对外提供插入(enqueueMessage)和读取(next)工作。读取本身附带删除操作。单链表在插入和删除上比较有优势。
enqueueMessage方法根据消息的延迟时间来进行的单链表的插入操作,next方法是一个无限循环,如果消息队列中没有消息,就会阻塞,当有新消息到来时,next方法就返回这条消息并将其从单链表中删除。

2.2 Looper
Looper以无限循环的形式去消息队列查找是否有新消息,如果有,就处理消息,否则就一直阻塞等待。
需要注意的是,Handler在创建时会采用当前线程的Looper来构造消息循环系统,Handler内部是通过ThreadLocal来实现的。
有一种情况是HandlerThread的使用情况。如果想让HandlerThread退出,则需要调用handlerThread.quit()。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
HandlerThread handlerThread = new HandlerThread(“新线程”);
handlerThread.start(); //创建HandlerThread后一定要记得start()
Handler handler = new Handler(handlerThread.getLooper()){
//重写handleMessage方法处理消息
//注意通过handler发送消息,就会在子线程中执行处理消息
};

ThreadLocal适用于某些数据以线程为作用域并且不同线程具有不同数据副本的场景。
ThreadLocal可以在不同线程中互不干扰地存储并提供数据,通过它可以获取每个线程的Looper。
ThreadLocal的神器功能介绍请看之前写过的一篇博文,点击这里。
(1)UI线程,即ActivityThread被创建时会初始化Looper,因此在主线程默认可以使用Handler。
(2)子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错。通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环。如下所示。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();

Looper的loop()方法极其重要。因为它消息循环系统才真正起作用。
loop方法就是一个死循环,跳出死循环的唯一条件是MessageQueue的next返回了null。当Looper的quit方法被调用时,MessageQueue的next就会返回了null。
loop方法会调用MessageQueue的next方法,next是一个阻塞操作,前面也讲过了。只有当next返回了新消息,Looper才会处理这条消息。这样Handler发送的消息最终又交给它的dispatchMessage方法来处理。但是Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的。这样就将逻辑切换到指定线程中去执行了。

Looper也是可以退出的,Looper提供quit和quitSafely来退出一个Looper。唯一的区别是,后者会处理完消息队列中已有的消息后才安全退出,前者直接退出。Looper退出后,通过Handler发送消息会失败,send方法返回false。在子线程中,手动创建的Looper在不需要时应quit退出,否则会此线程会一直处于等待状态。

2.3 Handler
Handler主要工作是消息的发送和接收。
消息的发送可以使用Handler的post的方法(最终还是通过send方法完成)将一个Runnable投递到Looper中去处理,send方法发送一个消息也是同理。
这里我们有必要对这两种方式进行说明。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
handler.post(new Runnable(){
@Override
public void run() {
//do something
}});

或者这种用法的变形,用途很广,功能是延迟3秒后从欢迎界面进入主界面。这里并不是开启了一个新的线程。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}, 3000);

post方法的使用如上所示,我们还知道Hanlder中也有一个handler.sendMessage(Messagemsg)方法,这两个方法有什么区别呢?
看一下handler.post(Runnable callback)方法的源码,很明显最终还是通过send方法完成的。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
再看一下sendMessageDelayed的源码:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

这里面有个关键就是方法getPostMessage(r)这个方法,他将Runnable转成一个Message,他内部到底干了什么呢?看一下他的源码:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

这里面就是将Runnable转化成一个Message,其他看他的代码很简单,就是先获取一个空消息Message.obtain(),然后将Message中私有变量callback的值设置成Runnable。

send方法被调用时,会调用MQ的enqueueMessage方法将这个消息放入消息队列中,MQ的next方法会返回这条消息给Looper,Looper发现新消息到来会处理之,最终消息交给Handler处理。dispatchMessage方法被调用,过程如下。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg);
}else{
if(mCallback!=null){
if(mCallback.handleMessge(msg)){
return;
}
}
handleMessage(msg);
}
}
首先检查Message的callback不为null,不为null就意味着,callback是一个Runnable对象(实际上就是Handler的post方法所传递的Runnable参数),这就是从post方法来实现的。就通过handleCallback来处理消息。handleCallback逻辑很简单,直接就是msg.callback.run(),执行我们在Runnable方法里重写的run方法。
其次检查mCallback不为空,调用mCallback的handleMessage方法来处理消息。
这是为了处理以下这种Handler的使用情况。CallBack可以用来创建一个Handler的实例但并不需要派生Handler的子类。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});

若我们直接返回了true,就不用再处理消息了。返回false,或者mCallback为空,会在最后执行Handler的handleMessage(msg)方法来处理消息。
这样我们就分析完了Android消息机制的整个流程。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

《Android开发艺术探索》读书笔记——Handler消息机制(ThreadLocal)

ThreadLocal是一个线程内部的数据存储类。它可以为各线程存储数据,同时只能由当前线程获取到存储的数据,对于其他线程来说则获取不到。它可以在不同线程中维护一套数据的副本,并且彼此互不干扰。

android开发学习(四)——anr产生的原理&如何避免,android消息机制入门, 网络图片查看器

ANR: application not response 应用程序无响应 anr 产生的原因:主线程 需要做很多重要的事情,响应点击时间,更新ui             ...

《Android开发艺术探索》--消息机制

介绍:在Android中,我们最经常使用的消息传递机制就是Handler+Looper+MessageQueue。这种机制常用于,将耗时任务在另一个线程中运行,而不影响主线程中UI的绘制,只有当任务完...

Android线程间通信的消息机制(Android开发艺术探索学习笔记)

参考文章: Android的消息机制之ThreadLocal的工作原理1.概述 Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和...

《Android开发艺术探索》笔记(2)Android消息机制

消息机制主要包含三个元素:Handler、MessageQueue、Looper 工作原理Hander被创建后,通过Handler的post方法将一个Runable投递到Handler内部的Loope...

《Android开发艺术探索第十章读书笔记》-Android消息机制

Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。 Handle来创建时会采用当前线程的Looper来构造消息循环系...

android开发之源码级分析(系统启动流程 & Handler消息机制 & AsyncTask机制)

All in all,because just begun!

《Android 开发艺术与探索》笔记——(10)Android 的消息机制

Android 消息机制主要是 Handler,Looper,MessageQueue 这三者。 其中 MessageQueue 是 Looper 内的对象,Looper 是 Handler 内的对象...
  • Gdeer
  • Gdeer
  • 2016-09-21 17:14
  • 130

Android 开发艺术探索——第十章 Android的消息机制

Android 开发艺术探索——第十章 Android的消息机制读书笔记Handler并不是专门用于更新UI的,只是常被用来更新UI概述Android的消息机制主要值得就是Handler的运行机制,H...
  • hdszlk
  • hdszlk
  • 2016-05-16 09:55
  • 3085

android消息机制EventBus

  • 2015-02-25 14:26
  • 44KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)