zoukankan      html  css  js  c++  java
  • Android开发——Android的消息机制详解

    1. 我们为什么需要Android的消息机制

     我们知道,Android规定访问UI只能在主线程中进行。若在子线程中访问UI,就会抛出异常。这个验证由ViewRootImplcheckThread方法来完成。

    为什么不允许在非主线程访问UI,这是因为AndroidUI控件不是线程安全的。并发访问会导致控件处于不可预期的状态。

    那为什么不对UI访问加上锁机制呢,原因如下:

    1)这显然会让UI访问的逻辑变得极其复杂;

    2)除了效率问题,锁机制还会阻塞某些进程的执行。

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

    本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52120086

     

    2. Android的消息机制结构

     Android的消息机制主要是指Handler的运行机制。Handler的运行需要底层MessageQueueLooper的支撑。

     

    2.1  MessageQueue

    MessageQueue采用单链表的数据结构存储消息列表对外提供插入(enqueueMessage)和读取(next)工作。读取本身附带删除操作。单链表在插入和删除上比较有优势。

    enqueueMessage方法根据消息的延迟时间来进行的单链表的插入操作,next方法是一个无限循环,如果消息队列中没有消息,就会阻塞,当有新消息到来时,next方法就返回这条消息并将其从单链表中删除。

     

    2.2  Looper

    Looper以无限循环的形式去消息队列查找是否有新消息,如果有,就处理消息,否则就一直阻塞等待。

    需要注意的是,Handler在创建时会采用当前线程的Looper来构造消息循环系统Handler内部是通过ThreadLocal来实现的。

    有一种情况是HandlerThread,HandlerThread 继承自Thread内部已经封装了Looper。并对外提供自己这个Looper对象的get方法,这就是它和普通Thread唯一不同的地方。具体关于HandlerThread的使用以及特性介绍请查看Android开发——HandlerThread以及IntentService详解

    ThreadLocal适用于某些数据以线程为作用域并且不同线程具有不同数据副本的场景。

    ThreadLocal可以在不同线程中互不干扰地存储并提供数据,通过它可以获取每个线程的Looper

    ThreadLocal的神奇功能介绍请看之前写过的一篇博文,Android开发——ThreadLocal功能介绍

    1UI线程,即ActivityThread被创建时会初始化Looper,因此在主线程默认可以使用Handler

    2)子线程默认是没有Looper的,Handler创建前,必须手动创建,否则会报错。通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环。如下所示。

    new Thread(){
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            Looper.loop();
        }
    }.start();
    

    Looperloop()方法极其重要。因为它消息循环系统才真正起作用。

    loop方法就是一个死循环,跳出死循环的唯一条件是MessageQueuenext返回了null。当Looperquit方法被调用时,MessageQueuenext就会返回了null

    loop方法会调用MessageQueuenext方法,next是一个阻塞操作,前面也讲过了。只有当next返回了新消息,Looper才会处理这条消息。这样Handler发送的消息最终又交给它的dispatchMessage方法来处理但是HandlerdispatchMessage方法是在创建Handler时所使用的Looper中执行的。这样就将逻辑切换到指定线程中去执行了。

     

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

     

    2.3  Handler

    Handler主要工作是消息的发送和接收

    消息的发送可以使用Handlerpost的方法(最终还是通过send方法完成)将一个Runnable投递到Looper中去处理send方法发送一个消息也是同理。

    这里我们有必要对这两种方式进行说明。

    handler.post(new Runnable(){
    @Override
    public void run() {
    //do something
    }});
    

    或者这种用法的变形,用途很广,功能是延迟3秒后从欢迎界面进入主界面。这里并不是开启了一个新的线程

    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方法完成的。

    public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
    }
    

    再看一下sendMessageDelayed的源码

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
    if (delayMillis < 0) {
    delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    

    这里面有个关键就是方法getPostMessage(r)这个方法,他将Runnable转成一个Message,他内部到底干了什么呢?看一下他的源码:

    private final Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
    }
    

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


    send方法被调用时,会调用MQenqueueMessage方法将这个消息放入消息队列中,MQnext方法会返回这条消息给LooperLooper发现新消息到来会处理之,最终消息交给Handler处理。dispatchMessage方法被调用,过程如下。

    public void dispatchMessage(Message msg){
        if(msg.callback!=null){
            handleCallback(msg);
        }else{
            if(mCallback!=null){
                if(mCallback.handleMessge(msg)){
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    首先检查Messagecallback不为null不为null就意味着,callback是一个Runnable对象(实际上就是Handlerpost方法所传递的Runnable参数),这就是从post方法来实现的。就通过handleCallback来处理消息。handleCallback逻辑很简单,直接就是msg.callback.run(),执行我们在Runnable方法里重写的run方法。

    其次检查mCallback不为空,调用mCallbackhandleMessage方法来处理消息。

    这是为了处理以下这种Handler的使用情况CallBack可以用来创建一个Handler的实例但并不需要派生Handler的子类。

    Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
    

    若我们直接返回了true,就不用再处理消息了。返回false,或者mCallback为空,会在最后执行HandlerhandleMessage(msg)方法来处理消息。

    这样我们就分析完了Android消息机制的整个流程。


  • 相关阅读:
    PageRank
    Map-Reduce基础
    Docker Hadoop LAMP安装配置教程
    HDFS Architecture
    Goole Search Auto Complete
    TopK-微博今日热门话题
    Hadoop开发环境搭建
    文本数据挖掘 Matrix67: The Aha Moments
    神经风格转换Neural Style Transfer a review
    神经风格转换 (Neural-Style-Transfer-Papers)
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461581.html
Copyright © 2011-2022 走看看