[Android]《Android开发艺术探索》学习笔记-Android的消息机制

从开发的角度来说,Handler是Android消息机制的上层接口。Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue是一个消息队列,内部存储了一组消息,以队列的形式对外提供插入和删除的工作,内部采用单链表的数据结构来存储消息列表。Lopper会以无限循环的形式去查找是否有新消息,如果有就处理消息,否则就一直等待着。线程是默认没有Looper的,使用Handler就必须为线程创建Looper。

一、Android的消息机制概述

Android规定访问UI必须在主线程中,ViewRootImpl的checkThread方法对UI操作做了验证,但Android又不建议在主线程做耗时操作,否则会导致ANR,所以我们需要Handler。

Android的UI控件是非线程安全的,所以不能多个线程同时访问,为什么不对UI控件访问加上锁机制呢?因为锁机制会让UI访问的逻辑变得复杂,然后锁机制会让UI执行的效率降低,因为锁机制会阻塞某些线程的执行

Handler创建完毕后,其内部的Looper和MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable投递到Handler中内部的Looper中去处理,也可以通过Handler的post方法发送一个消息,同样也在Looper中处理。其实post也是通过send完成的。Handler的send方法执行时,会调用MessageQueue的enqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或Handler的handleMessage就会被调用。注意Looper是运行Handler所在的线程中的,这样一来业务逻辑就切换到了Handler所在的线程中去执行了。

 

二、Android的消息机制分析

1、ThreadLocal的工作原理

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,对于其他线程来说无法获得数据。比如对于Handler来说,它需要获取当前线程的Looper,而Looper的作用于就是线程并且不同的线程具有不同的Looper,通过ThreadLocal可以轻松实现线程中的存取。 

ThreadLocal的另一个使用场景是复杂逻辑下的对象传递,通过ThreadLocal可以让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取到监听器。

ThreadLocal原理:不同线程访问同一个ThreadLocal的get方法,ThreadLocal的get方法会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找对应的Value值。

 public void set(T value) {
     Thread currentThread = Thread.currentThread();
     //通过values方法获取当前线程中的ThreadLocal数据——localValues
     Values values = values(currentThread);
     if (values == null) {
         values = initializeValues(currentThread);
     }
     values.put(this, value);
 }

上面的set方法中,通过valuse方法来获取当前线程中的ThreadLocal数据,在Thread内部有一个专门存储线程的ThreadLocal的数据:localValues,在localValues内部有一个数组:private Object[] table,ThreadLocal的值就存在这个数组中。ThreadLocal的值在table数组中的存储位置总是ThreadLocal的reference字段所标识的对象的下一个位置。

 public T get() {
     // Optimized for the fast path.
     Thread currentThread = Thread.currentThread();
     Values values = values(currentThread);//找到localValues对象
     if (values != null) {
         Object[] table = values.table;
         int index = hash & values.mask;
         if (this.reference == table[index]) {//找到ThreadLocal的reference对象在table数组中的位置
             return (T) table[index + 1];//reference字段所标识的对象的下一个位置就是ThreadLocal的值
         }
     } else {
         values = initializeValues(currentThread);
     }
     return (T) values.getAfterMiss(this);
 }

从ThreadLocal的set/get方法可以看出,它们所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的set/get方法,它们对ThreadLocal的读/写操作仅限于各自线程的内部。

2、消息队列的工作原理

消息队列指的是MessageQueue,主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取分别为enqueueMessagenext,enqueueMessage的作用是往MessageQueue插入一条消息,next的作用是从消息队列中取出一条消息并从消息队列移除。MessageQueue其实是个单链表的结构,在插入和删除有优势。

在源码中,enqueueMessage()的源码实现主要操作就是单链表的插入操作,next()的源码实现是从单链表中取出一个元素的操作,next()方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next()方法会返回这条消息并将其从单链表中移除。

3、Looper的工作原理

Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在那里。通过Loop.prepare()方法即可为当前线程创建一个Looper,再通过Looper.loop()开启消息循环。prepareMainLooper()方法主要给主线程也就是ActivityThread创建Looper使用的,本质也是通过prepare方法实现的。Looper提供quit和quitSafely来退出一个Looper,区别在于quit会直接退出Looper,而quitSafely会把消息队列中已有的消息处理完毕后才安全地退出。 Looper退出后,这时候通过Handler发送的消息会失败,Handler的send方法会返回false。

Looper的loop是个死循环,唯一跳出循环的方式是MessageQueue的next方法返回null,当Looper的quit方法被调用时,就会调用MessageQueue的quit或quitSafely方法来通知消息队列退出,next方法返回null。也就是说Looper必须要退出,不然会一直循环,loop调用next,没有消息时next阻塞线程,导致loop也阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给Handler来处理了。

4、Handler的工作原理

Handler的工作主要包含消息的发送和接收过程。通过post的一系列方法和send的一系列方法来实现。Handler发送过程仅仅是向消息队列中插入了一条消息。MessageQueue的next方法就会返回这条消息给Looper,Looper拿到这条消息就开始处理,最终消息会交给Handler的dispatchMessage()来处理,这时Handler就进入了处理消息的阶段。dispatchMessage()方法运行在Looper所在的线程上

 public void dispatchMessage(Message msg) {
     if (msg.callback != null) {
         //Message的callback是一个Runnable,       
        //也就是Handler的 post方法所传递的Runnable参数
         handleCallback(msg);
     } else {
         //如果给Handler设置了Callback的实现,
         //则调用Callback的handleMessage(msg)
         if (mCallback != null) {
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         //调用Handler的handleMessage方法来处理消息,
         //该Handler子类需重写handlerMessage(msg)方法
         handleMessage(msg);
     }
 }

 private static void handleCallback(Message message) {
 //直接执行Runnable中的run()方法
     message.callback.run();
 }

 public interface Callback {
 //不想继承Handler子类,可以通过Callback来实现handleMessage
     public boolean handleMessage(Message msg);
 }

 

三、主线程的消息循环

Android的主线程就是ActivityThread,主线程的入口方法为main(String[] args),在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。

public static void main(String[] args) {
  ... 
  Process.setArgV0("<pre-initialized>");

  Looper.prepareMainLooper();//创建主线程的Looper

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  if (sMainThreadHandler == null) {
      sMainThreadHandler = thread.getHandler();
  }

  AsyncTask.init();

  if (false) {
      Looper.myLooper().setMessageLogging(new
              LogPrinter(Log.DEBUG, "ActivityThread"));
  }

  Looper.loop();//开启looper

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

主线程的消息循环模型:ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityTread中去执行,即切换到主线程中去执行。

发表评论

电子邮件地址不会被公开。 必填项已用*标注