Android个人笔记之Android的消息机制

作者: Cooke_ | 来源:发表于2016-09-25 21:11 被阅读463次

    概述

    Handler在开发中最常用的就是拿来更新UI了。Handler的运行需要MessageQueue和Looper的支撑。Handler创建的时候会采用当前线程的Looper构造消息循环系统。但线程是默认没有Looper的,需要我们手动创建。除非是主线程,也就是UI线程,它就是ActivityThread。ActivityThread被建立的时候系统就会初始化Looper,所以主线程可以默认使用Handler。Handler创建完毕后,通过Handler的post方法将一个Runnable对象投递到Looper中处理,或者用handler的send方法发送消息,其实post方法最终也是通过send方法调用。

    ThreadLocal

    ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程存储数据,所以也只能在指定线程中获取相、应的数据,其他线程则没有办法。平常用到ThreadLocal的地方不多,但有时也挺好用。如果某些数据是以线程为作用域并且不同线程有不同数据副本时,就可以使用ThreaLocal。比如Looper。不同线程有不同的Looper,通过ThreaLocal就可以轻松实现Looper在不同线程中的存取。
    我们演示一下ThreadLocal的用法。

    private ThreadLocal<boolean> mBooleanThreadLocal = new ThreadLocal<boolean>();
    

    接着在主线程,子线程1,子线程2中分别设置和获取它的值。

    mBooleanThreadLocal.set(true);
    Log.d("TAG","(Thread main)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
    
    new Thread(){
    @override
    public void run(){
    mBooleanThreadLocal.set(false);
    Log.d("TAG","(Thread1)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
      }
    }.start();
    
    new Thread(){
    @override
    public void run(){
    mBooleanThreadLocal.set(false);
    Log.d("TAG","(Thread2)mBooleanThreadLocal ="+mBooleanThreadLocal.get());
      }
    }.start();
    
    

    上面代码输出为

    (Thread main)mBooleanThreadLocal =true;
    (Thread1)mBooleanThreadLocal =false;
    (Thread2)mBooleanThreadLocal =null;
    

    看完你就知道ThreadLocal的神奇之处。同一个ThreadLocal对象得到的值却不同。其实是因为不同线程调用ThreadLocal的get方法,ThreadLocal内部会从各自线程取出一个数组,再从数组中根据当前ThreadLocal的索引查找对应的value值。显然不同线程的数组是不同的。所以ThreadLocal在不同线程中维护一套数据的副本是互不干扰的。

    MessageQueue

    MessageQueue主要包括两个操作,插入和读取,对应的方法为enqueueMessage和next。enqueueMessage的作用就是往消息队列中插入一条消息。next的作用是取出一则消息读取,并从消息队列中移除。MessageQueue虽然叫做消息队列,实际上却是用单链表的数据结构,因为执行插入和删除效率比较高。

    Looper

    Looper创建时MessageQueue会跟着创建。并把当前线程的对象保存起来。
    在线程中使用Looper的方式如下,注意这里的Handler并不能更新UI。

    new Thread(){
    @override
    public void run(){
    Looper.prepare();//创建Looper对象
    Handler handler = new Handler();
    Looper.loop();//Looper开始工作
      }
    }.start();
    

    Looper还提供了退出的方法,quit和quitSafely。调用quit方法会直接退出Looper,而quitSafely只是设定一个退出标记,然后等消息队列中的已有消息全部处理完毕后才退出。Looper退出后,Handler发送的消息会失败。如果在子线程中手动创建Looper,那么所有事情完毕后应调用quit方法终止消息循环。而如果退出Looper后,线程也就被终止了。
    Looper.loop()是一个死循环,只有MessageQueue的next方法返回了null才能跳出循环。当Looper的quit方法调用时Looper会调用MeassageQueue的quit方法来通知消息队列退出,此时next方法就会返回null。

    Handler的工作原理

    先看一下发送消息过程的源码

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        } 
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 
      MessageQueue queue = mQueue;    
      if (queue == null) {        
        RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");        
        Log.w("Looper", e.getMessage(), e);        
        return false;    
      }    
      return enqueueMessage(queue, msg, uptimeMillis);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      msg.target = this;    
      if (mAsynchronous) {        
        msg.setAsynchronous(true);    
      }    
      return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    可以发现,Handler发送消息的过程只是向消息过程中插入一则消息。 MessageQueue的next方法会返回这条消息给Looper,Looper收到后开始处理,最终由Looper交给Handler处理。,这时Handler的dispatchMessage方法被调用。

    public void dispatchMessage(Message msg) { 
      if (msg.callback != null) {        
        handleCallback(msg);    
      } else {  
        if (mCallback != null) {            
          if (mCallback.handleMessage(msg)) {                
            return;            
          }        
        }
        //处理sendMessage();方法发送的消息        
        handleMessage(msg);    
      }
    }
    

    看上面的代码,Message的callback是一个Runnable对象,就是post方法传递过来的Runnable对象。handleCallback的方法很简单,就是执行run方法。

    private static void handleCallback(Message message) {
     message.callback.run();
    }
    

    Callback是一个接口。定义为

    public interface Callback { 
      public boolean handleMessage(Message msg);
    }
    

    Callback的意义在于可以创建Handler的实例而不用派生Handler的子类。使用方式如下

    Handler handler= new Hanlder(Callback);
    

    平常我们一般都是创建Handler的子类并重写handleMessage方法。Callback为我们提供了另一种方式。

    我们借助一张图片来理解一下Hanlder消息处理的流程,相信不难理解。

    Handler消息处理流程

    以上总结大部分来自《Android开发艺术探索》,想要理解更仔细请翻阅原书。

    相关文章

      网友评论

        本文标题:Android个人笔记之Android的消息机制

        本文链接:https://www.haomeiwen.com/subject/ulsfyttx.html