Android线程间通信 ——Handler

Message

Message中一些常见的参数与API

public final class Message implements Parcelable{
    public int what; // 消息的id
    public int arg1; // 整数参数1
    public int arg2; // 整数参数2
    public Object obj; // 任意类型参数
    int flags; // 消息标记:正在使用异步还是同步
    Bundle data; //消息附带的额外数据
    long when; // 消息被处理的时间
    Handler target; // 消息的接收与处理者
    Messge next; // 下一个消息的指针
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;
    public static Messgae obtain(){} // 复用消息池中的消息
    void recycleUnchecked(){} // 回收并清理消息(msg.target = null;sPool = this)
    public void sendToTarget(){target.sendMessage(this);}
}
  1. 由于Message定义了下一个消息的指针,因此它组成了一个消息单链表;
  2. 在Looper.loop()从消息队列中读取一个消息后,首先调用msg.target.dispatchMessage(msg) 将消息回传给 handleMessage(),最后调用msg.recycleUncheckd() 对消息进行清理回收。
  3. 使用handler.obtainMessage()复用消息时,其实是从消息池中获取一个消息再利用。
MessageQueue

Message中一些常见的参数与API

public final class MessageQueue{
    public boolean mQuitAllowed;
    Message mMessages; // 当前要处理的消息
    private boolean mBlocked; // 表明线程是否阻塞在pollOnce上
    private boolean mQuitting; // 是否主动退出循环
     
    private void native nativePollOnce() // 阻塞线程
    private void native static nativeWake; // 唤醒被阻塞的线程
    
    public void enqueueMessage(msg){} // 添加消息至消息队列
    public int postSyncBarrier(){} // 发送一个同步屏障,阻塞同步消息
    Message next(){} // 读取消息
}

  1. MessageQueue采用链表来管理消息,其mMessages是当前要处理的消息;
  2. 调用next()函数从消息链表中读取一个消息时,若队列中没有消息,则会阻塞在nativePollOnce()上;若队列中有消息,则会检测当前消息是不是同步屏障 ,若是则过滤所有的同步消息,只迭代异步消息,然后从链表中删除并得到该消息。
  3. 调用enqueueMessage()向消息队列中添加消息,若队列中还没有消息,则直接作为消息链表的head,并调用nativeWake()唤醒阻塞在nativePollOnce()上的线程;若消息队列中有消息,比较 msg.when ,按照时间顺序添加进队列,检测是否需要调用nativeWake()。
Handler

Handler中的重要参数和API

public class Handler{
    final Looper mLooper; // 循环读取消息队列中的消息
    final MessageQueue mQueue; // Looper内置的消息队列
    final Callback mCallback; // 处理消息的回调
    // 指定Looper的构造函数
    public Handler(Looper looper){
        this.mLooper = looper;
        this.mQueue = looper.mQueue;
        ***
    }
    // 不指定Looper的构造函数
    public Handler(){
        this.mLooper = Looper.myLooper(); // 通过ThreadLocal.get()获取本线程的Looper
        if(mLooper == null){
            throw RuntimeExcption("");
        }
        this.mQueue = mLooper.mQueue;
        ***
    }
    // 向消息队列添加消息
    // post系列
    pulbic final boolean postDelayed(Runnable runnable, long delayMillis){
    
        return sendMessageDelayed(getPostMessage(runnable), delayMillis);
    }
    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r; // 指定了回调处理者
        return m;
    }
    // send系列
    public final boolean sendMessage(Message msg){
         return sendMessageDelayed(getPostMessage(runnable), 0);
    }
    // 移除指定消息
    public final void removeMessage(int mhat){
        mQueue.removeMessages(this, what, null);
    }
    // 移除所有的消息
    public final void removeCallbacksAndMessages(Handler handler, Object obj){
        mQueue.removeCallbacksAndMessage(this,obj);
    }
    // 复用Message
    public final Message obtainMessage(){
        return Message.obtain();
    }
    // 分发消息
    public void dispatchMessage(Message msg){
        if(msg.callback != null){ // 优先处理指定了消息处理者的消息
            handleCallback(msg);
        } else {
            if(mCallback != null){
                mCallback.handleMessage(msg);
            }else{
                handleMessage(msg);
            }
        }
    }
    // 处理消息的回调,子类必须实现
    public void handleMessage(Message msg){}
    
}
  1. Handler必须在有Looper的线程中创建,可以在任意线程发送消息,处理消息的线程都是其对应的Looper所在的线程;由对应线程的Looper.loop()循环从消息队列中读取消息,接着调用 dispatchMessage 分发并处理消息;
  2. post系列的函数为待发送的消息指定了回调处理者Runnable,因此该消息交由handleCallback(msg)处理;send系列函数则全部交由handleMessage(msg)处理。
  3. 若在Activity或者Fragment中使用Handler向消息队列添加了消息,则在onDestrory时,主动调用removeMessage()对消息进行清理回收,防止内存泄露;
Looper

一个线程只能有一个Looper用来作为线程循环,新创建的Looper实例将保存到该线程的线程本地存储(ThreadLocal)中。

Looper拥有一个消息队列MessageQueue,该消息队列在Looper的构造方法中创建,属于该Looper对象所有。一个Looper只能有一个MessageQueue.

线程通过Looper.loop()循环处理Looper维护的MessageQueue中的Message,消息发送到那个Looper的消息队列中和如何处理该消息,则有消息发送者指定。指定了Looper就意味着指定了处理消息的线程;为消息指定的Handler则决定了用户如何处理该消息。

源码分析:


public final class Looper{
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // 线程本地变量
    private static Looper sMainLooper; // 主线程的Looper实例
    final MessageQueue mQueue; // Looper维护的消息队列
    final Thread mThread; // Looper所处的线程
    private Looper(boolean quitAllowed){
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    public static void prepare(){
        // 确保一个线程只能拥有一个Looper实例
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 通过ThreadLocal维护一个Looper实例
        sThreadLocal.set(new Looper(true));
    }
    public static void prepareMainLooper(){
        prepare();
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 将ThreadLocal维护的所属主线程的Looper实例改为类变量。
            sMainLooper = myLooper();
            // 由此可见,在Activity中即便不使用静态+弱引用的方式创建Handler也不一定引起内存泄露。
        }
    }
    public static Looper myLooper(){
       return sThreadLocal.get(); 
    }
    // 循环读取维护的消息队列中的消息
    
    public static void loop(){
        final Looper me = myLooper();
        if(me == null){
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ****
        for(;;){
            Message msg = queue.next(); // 可能会阻塞
            if(msg == null){ // 表明线程退出    
                return;
            }
            ***
            try{
                // 分发并处理消息msg
                msg.target.dispatchMessage(msg);
                
            }finally{
                
            }
            // 清理并回收该消息,将该消息添加进消息池
            msg.recyleUncheckd();
        }
    }
}

问题:

  1. 如果msg.next会阻塞主线程,那为什么程序运行时没感到卡顿那?
    Handler的阻塞是通过底层的Looper.pollOnce(long delayMillis)实现的。pollOnce采用 epoll 机制(NIO机制的一种),在读取端阻塞时不会消耗CPU资源,所以不存在竞争资源的问题,因此不会感到卡顿。
  2. Handler必须在一个含有Looper的线程中创建,为什么这么设计?
    首先,我们知道,Handler是消息的发送者也是消息的处理者,而消息是消息队列维护的,消息队列又是在Looper的构造方法中创建的,也就是说Looper自己维护了消息队列。因此,Handler必须在一个初始化并执行了Looper的线程中创建。

总结

Handler通信机制采用循环等待/唤醒机制,在一个线程中通过handler的post/send系列函数向Looper维护的消息队列中添加消息时,会唤醒阻塞在nativePollOnce处的线程,在Looper.loop循环中读取消息,然后使用handler分发处理该消息.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值