一、概述

  • 1、Handler的主要作用是将某个任务切换到指定的线程中去执行

    • eg:子线程中无法更新UI,需切换到主线程
      • ViewRootImpl通过 方法检查更新UI操作是否是在主线程当中
      • 原因:Android的UI是线程不安全的,存在并发访问的问题。加锁也不合适
        • 加锁会让UI访问的逻辑变得复杂
        • 加锁会降低UI访问的效率,因为锁会阻塞某些线程的执行
  • 2、消息机制各部分之间的联系:

    Android开发艺术探索》学习笔记之Android的消息机制-LMLPHP

    • Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上
    • Handler是Android消息机制的上层接口
      • Android消息机制主要是指Handler的运行机制
      • 其运行需要底层的MessageQueueLooper的支撑
  • 3、Handler的工作原理:

    • (1)每个Hanlder都关联了一个线程,每个线程内部都维护了一个消息队列MessageQueue,这样Handler实际上也就关联了一个消息队列
      • 在执行new Handler()的时候,默认情况下Handler会绑定当前代码执行的线程
    • (2)创建时会采用当前线程的Looper来构建内部的消息循环系统
      • 如果当前线程没有Looper,系统会报错
      • ActivityThread(即主线程)被创建时就会初始化Looper,所以默认可使用Handler
      • 非主线程默认是没有Looper的,使用时需要去创建
      • Handler初始化之前,Looper的初始化必须先完成
    • (3)总结:Handler - MessageQuene和Looper - Thread 是在每个线程都一一对应的
  • 4、消息机制大致的工作过程:
    Android开发艺术探索》学习笔记之Android的消息机制-LMLPHP

    • 首先Handler通过发送一个消息(或发送一个Runnable)到Handler内部的Looper当中处理
      • post方法最终也是通过调用send方法
    • 当Handler的send方法被调用的时候,它会调用将这个消息放到消息队列里,当Looper发现有新消息到来时,就会处理这个消息
    • 最终消息中的会被调用。
      • Looper是运行在创建Handler所在的线程中的,这样Handler中(在Thread 1中调用Thread2中创建的Handler)的业务逻辑就被切换到创建Handler的线程(Thread 2)中执行了

二、消息机制各大组成部分详解

1、ThreadLocal
  • 定义:ThreadLocal是一个线程内部的数据存储类。

    • 通过它可以在指定线程中存储数据
    • 只有在指定线程中才可以获取到存储的数据
  • 使用场景:

    • (1)一般来说,当某些数据(eg:同一个对象)是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal
      • 如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper
    • (2)复杂逻辑下的对象传递。
      • eg:监听器的传递:通过ThreadLocal让监听器作为线程内的全局对象,在线程内只要通过get方法就可以获取到监听器
  • 使用:

    • ThreadLocal是一个泛型类
    ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
    
    • 在不同的线程中可以调用这同一个去设置相应的value
  • 原理:不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前的ThreadLocal的索引去查找出对应的value值

    • 不同线程中的数组是不同的,ThreadLocal的set和get方法所操作的对象都是当前线程的LocalValues对象的table数组
    • set方法解析
      • 在Thread类中有一个成员专门用于存储线程的ThreadLocal的数据:

      • localValues中有一个object[] table用于存放ThreadLocal在每个线程中的数据,调用存入

        • ThreadLocal的值在table数组中的存储位置总是为ThreadLocal的reference字段所标识的对象的下一个位置
        table[index] = key.reference;
        table[index + 1] = value;
        
      • ThreadLocal对象set方法源码

      	public void set(T value){
      		ThreadLocal currentThread = Thread.currentThread();  //获取到当前线程对象
      		Values values = values(currentThread);               //获取到当前线程下的values对象
      		if(values == null){                                  //如果values数组为null,则对他初始化
          		values = initializeValues(currentThread);
      		}
      		values.put(this, value);                              //将values中的ThreadLocal数据
          	 	                                                //存入Object[] table数组当中
      	}
      
2、Message
  • 获取Message

    • (1)Message msg = new Message();
      • 不建议使用直接构造
    • (2)Message msg = Message.obtain(handler);
    • (3)Message msg = handler.obtainMessage();
  • 向Message中装填数据及取出

    • (1) 设置识别码

      • 插入:
        msg.obj = 1
        
      • 取出:
        • what是我们自定义的一个Message的识别码,以便于在Handler的handleMessage方法中根据what识别出不同的Message,以便我们做出不同的处理操作
    • (2) 设置简单值

      • 插入:
        msg.arg1 = 123;
        msg.arg2 = 321;
        
      • 取出:
        msg.getArg1/2();
        
    • (3) 传入对象

      • 插入:
        //可以通过给obj赋值Object类型传递向Message传入任意数据
        msg.obj = null;
        
      • 取出:
    • (4) 通过Bundle传输

      • 插入:
        //可以通过setData方法向Message中写入Bundle类型的数据
        msg.setData(null);
        
      • 取出:
        //可以通过和getData方法从Message中读取Bundle类型的数据
        Bundle data = msg.getData();
        
3、MessageQueue
  • 定义:包含消息的插入()和读取()两个操作

    • 实际的数据结构并不是队列而是单链表
  • 使用:不需要直接使用这两个方法,已封装。

    • enqueueMessage():插入操作,往消息队列中插入一条消息。
    • next:读取操作,从消息队列中去除一条消息并将其从消息队列中移除。
  • 源码解析:

    • enqueueMessage和next方法中都存在死循环
    for(;;)  //死循环
    
4、Looper
  • 定义:消息循环。它会不停地从MessageQueue中查看是否有新的消息,如果有新消息就会立刻处理,否则就会一直堵塞在那里

  • 构造方法:

    • Looper的构造方法是私有的,只能通过工厂方法 这个静态方法获取当前线程所绑定的Looper
      • 因为线程与Looper是一对一绑定的
    private Looper(boolean quitAllowed) {
        // 创建MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        // 保存当前线程对象
        mThread = Thread.currentThread();
    }
    
  • 保存对当前线程的引用

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
  • 使用:

    • (1)为一个线程创建Looper
      new Thread("Thread#2"){
          @Override
          public void run(){
              Looper.prepare();
              Handler handler = new Handler();
              Looper.loop();
          }
      }.start();
      
      • :为当前线程创建一个Looper(将Looper绑定到当前线程)
        • Looper.prepareMainLooper:给ActivityThread创建Looper使用,内部调用prepare方法。同时提供 用于在任何地方获取到主线程的Looper
      • :开启消息循环
    • (2)退出Looper:
      • :直接退出Looper
      • :设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全退出
  • 原理:

    //loop方法关键代码
    
    ...
    
    for(;;) {
        Message msg = queue.next();
        if (msg == null) {
            return;                        //在每次进入循环体的开始首先进行消息的判空
        }
    
        ...
    
        msg.target.dispatchMessage(msg);    //mag.target是发送这条消息的Handler对象
                                            //Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的
                                            //这样就实现了切换线程的逻辑
        ...
    }
    
    ...
    
    
    • loop方法是一个死循环,唯一跳出的方法是MessageQueue的next方法返回了null
    • 当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit方法或quitSafely方法来通知消息队列退出
      • 当消息队列被标记为退出状态时,他的next方法就会返回null
5、Handler
  • 定义:主要包含消息的发送和接收过程

    • 发送消息源码调用过程:
      Android开发艺术探索》学习笔记之Android的消息机制-LMLPHP
  • 使用:

    • (1)创建Handler
      • 法1:派生一个Handler的子类并重写其方法来处理具体消息(最常使用)
      • 法2:通过Callback
        Handler handler = new Handler(callback);
        
        • Callback是一个接口
          public interface Callback {
              public boolean handleMessage(Message msg);
          }
          
      • 法3:通过一个特定的Looper来构造Handler
        public Handler(Looper looper) {
            this(looper, null, false);
        }
        
    • (2)消息的发送:send的一系列方法(或使用post的一系列方法)
      • post最终也会调用send的方法
      • 发送消息的过程仅仅是向消息队列中插入了一条消息
  • 源码解析:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);    //处理消息
        }
    }
    
    • (1)首先检查Message的callback是否为null,不为null就通过方法处理消息
      • Message的Callback是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数
      //handleCallback源码
      
      private static void handleCallback(Message message) {
          message.callback.run();
      }
      
    • (2)其次检查mCallback是否为null,不为null就通过处理消息
      • Callback是个接口,源码见 使用-法2
    • (3)最后如果前两个都为null才会调用 方法处理消息
10-04 11:15