理解Handler机制

Posted by 阿呆 on 2018-12-28

What Is Handler

从开发者的角度而言,Handler是Android消息机制的上层接口,用于进程内不同线程之间的通信。(可以了解下线程间通信的常用方式)

What’s Handler Use For

更新UI:
我们在日常开发种,用的最多的便是利用Handler来更新UI(注意,在子线程种更新UI是不准确的说法)。可以了解一下Handler的其它用处

UI操作验证

1
2
3
4
5
void checkThread(){
if( mThread != Thread.currentThread() ){
throw new calledFormWrongThreadExeception (
“Only the original thread that created a view hirrarchy can touch its views .)
}

How It Worked

从我的理解而言,Handler像一个人的左右手,左手将一个个的鸡蛋放在篮子里(有序性),右手则从篮子里将鸡蛋一个个取出。左手和右手虽然都是你的,但是它们工作在不同的线程(同一个变量可以由不同的线程使用,这个可以理解吧,它们分别对应于消息的发送和消息的接收),而这个篮子(对应于MessageQueue)就充当了沟通两只手的一个媒介(将任务由子线程转移到主线程)

Relative Conception

MessageQueue

消息队列,内部存储结构是一个单链表

Looper

消息循环

  • MessageQueue只是一个存储单元,它不能去处理消息,而Looper则填补了这个功能
  • Looper会以无限循环的形式去查找是否有新消息,有的话就处理,无则等待
  • 线程默认是没有Looper的,如果要使用Handler,需要为该线程创建 Looper (UI线程就是ActivityThread,在创建的时候,就会初始化 Looper)

Looper的创建:

  • Looper.prepare() --> looper.loop() /启动消息循环
    prepareMainLooper VS getMainLooper/可在任何线程获取 (主线程Looper的特殊性)
    在子线程中创建完 Looper后,应该在所有消息处理完毕的时候,退出Looper,不然线程就会一直处于等待的状态,而退出Looper过后,线程会立即终止
  • loop #Looper
    loop方法是一个死循环(除非MessageQueue的next返回null),loop会调用
    MessageQueue的next方法(这是一个阻塞方法),要么读取到新的消息,要么等待,
    要么返回null( 当Looper的quit方法被调用,消息队列被标记为退出状态,next返回
    null)

ThreadLocal

1. 功能
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据
2. 必要性
如果不使用 ThreadLocal,那么就需要提供一个全局的哈希表供Handler查找指定线程 的Looper,不是良好的封装
其它:
ThreadLocal还可以用于复杂逻辑下的对象传递,比如监听器的传递

理解 ThreadLocal 的实现方式有助于理解 Looper 的工作原理

Process

MessageQueue主要包含插入和读取两个操作,读取的操作伴随着删除(出队列),相关的方法是 enqueueMessage 和 next 方法

Handler的工作原理
Hanlder的工作主要是以下两点:

  • 消息的发送
    通过post 的一系列方法以及send的一系列方法来实现,post最终也是通过send
  • 消息的接收

那你可能会有疑问,发送完了就接收,那还发送个啥,直接处理不就行了么?肯定不行,因
为要排队,加入队列和取出队列的操作,涉及到 Looper 和 MessageQueue。Handler的主要任务就是将要处理的任务,以有序的方式排好队,交给目标线程,这样就很好地避免了因为资源的竞争而产生的一些不安全的操作,遵循的原则就是同一时刻只有一个任务被目标线程处理,待这个任务处理完毕,再接收下一个任务。

详解Send过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public final boolean sendMessage( Message msg ){
//这样设计挺好的sendMessageDelayed通用的接口,当delayed=0就相当sendMessage
return sendMessageDelayed( msg,0 );
}

public final boolean sendMessageDelayed( Message msg, long delayMillis){
sendMessageAtTime( msg, SystemClock.uptimeMillis()+delayMillis );
}
// uptimeMillis(系统的当前时间,从开机到现在不包括手机睡眠的时间)
//那么uptimeMillis+delayeMillis其实就是这个消息应该发送的时间点

//判断mQueue是否为空
public boolean sendMessageAtTime ( Message msg, long uptimeMillis {
MessageQueue queue = mQueue;
if( queue==null ){
RuntimeException e = new RuntimeException( this +”sendMessageAtTime()
called with no mQueue”);
Log.d(“Looper”,e.getMessage, e);
return false;
}
return enqueueMessage( queue,msg,uptimeMillis );
}
//mQueue不为空则发送消息
private boolean enqueueMessage( MessageQueue queue,Message msg, long uptimeMillis){
msg.target = this; //让msg携带Handler的引用
if( mAsynchronous){
msg.setAsynchronous(true);
}
return queue.enqueueMessage( msg,uptimeMillis );
}

Handler的发送消息的过程仅仅是以上,向消息队列中插入一条消息,一旦消息队列不为空,MessageQueue的next方法就会返回最前面的消息给Looper,Looper收到消息就开始处理了,最终消息由Looper交回Hanlder处理,即Handler的 dispatchMessage方法会被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage ( Message msg ){
if ( msg.callback != null ){
handleCallback ( msg );
} else{
if ( mCallback!=null ){ // mCallback 是一个Callback接口的实现
if ( mCallback.handleMessage (msg) )
return ;
}
handleMessage (msg) ;
}
}

private static void handleCallback( Message msg ){
msg.callback.run();
}
  • 其实就是分几种情况
  1. msg内部包裹了一个callback(Runnable对象),那直接callback.run就可以了
  2. 如果没有包裹,那就看mCallback是否被初始化,Handler创建的时候,是否传入了一个CallBack接口(需实现它的HandleMessage 方法)实例,有的话,那就直接调用
  3. 如果自身的 mCallback 变量没有在创建的时候被赋值,则调用重写的Handler的handleMessage方法
  • 2与3的处理有点类似于Thread的创建过程,既可以传入 Runnable对象,也可以重写Thread的run方法
1
2
3
4
5
6
7
8
9
10
11
12
13
Handler handler = new Handler ( new Callback(){
@Override
public boolean handleMessage(Message msg){
//your code here
}
}) //这个传入的 Callback 对象会赋值给 Hanlder内部的 mCallback
或者
Handler handler = new Handler(){
@Override
public void handleMessage( Message msg ){
// your code here
}
}

Handler的一个特殊的构造方法

Handler mHandler = new Handler(looper);
looper可以通过 Thread.getLooper()获取到,前提是目标线程初始化了 Looper,这个构造方法一般用于在线程外部构造与该线程关联的 Handler。可以与HanlderService这一章结合起来看

Handler In MainThread

主线程的消息循环

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

补充 # 需要进一步学习进程和线程相关知识才能理解
ActivityThread 的 handler是 ActivityThread.H,

  • ActivityThread.H内部定义了一组消息类型,主要包含了四大组件的启动和暂停等过程
  • ApplicationThread:
    ActivityThread通过 ApplicationThread 和 AMS 进行进程间通信,AMS以进程间通信
    的方式完成 ActivityThread的请求后回调ApplicationThread中的Binder方法,然后
    ApplicationThread会向H发送消息, H收到消息后会将 ApplicationThread中的逻辑切
    换到ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息
    循环模型