android 中 Handler 模块,Handler 和 Message 源码分析

我们在使用线程切换时,经常会遇到下面这种代码的写法

    final static int ONE_CODE = 1;

    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.arg1){
                case ONE_CODE:
                    Toast.makeText(getApplicationContext(), "arg1 : " + ONE_CODE +"  obj : "+ msg.obj, Toast.LENGTH_LONG).show();
                    break;
            }
        }
    };

    void test(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message(); // Message msg = Message.obtain();
                msg.arg1 = ONE_CODE;
                msg.obj = "one";
                mHandler.sendMessage(msg);    
            }
        }).start();
    }

我们定义了 Handler,在它方法中接收数据,然后做UI的操作,test() 中,子线程做了耗时操作,然后把数据封装到 msg 中,通过 mHandler 来完成线程切换,我们注意看子线程中第一行代码, Message msg = new Message(),这是 new 一个对象,但是稍微有点经验的都会用后面的代码 Message msg = Message.obtain(),这里面有什么区别吗?我们看看 Message

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public Messenger replyTo;
    public int sendingUid = -1;
    
        static final int FLAG_IN_USE = 1 << 0;
        static final int FLAG_ASYNCHRONOUS = 1 << 1;
        static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    int flags;
    
    long when;
    Bundle data;
    Handler target;
    Runnable callback;
        Message 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;

    private static boolean gCheckRecycle = true;

    public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }

    void recycleUnchecked() {
        
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = -1;
            when = 0;
            target = null;
            callback = null;
            data = null;

            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
}

Message 实现了 Parcelable 接口,说明是可以被序列化的,看看上面的成员变量, what、arg1、arg2 三个int的值,上面例子中,ONE_CODE 是个常量,我们可以用它作为 msg 的标签,在子线程中和 Handler 中形成映射关系,我们例子中用的是 arg1,实际上按照标准的写法,应该是用 what,它本来就是定义用拉力区分标识的,替换两行代码,msg.what =ONE_CODE; switch (msg.what){ ...} 即可。arg1 和 arg2 是我们用来传递 int 参数的,上面这样写,是为了说明,这几个值的作用,某些方面是相同的。如果要传递比较复杂的数据,使用 Object obj 或 Bundle data 这两个; Messenger replyTo 是用来跨进程通讯使用,前面讲 Messenger replyTo 这一章时提到过; int flags 是用来标识当前 Message 是否正在使用,上一章中 MessageQueue 中的 enqueueMessage(Message msg, long when) 方法中,msg.markInUse() 就改变了标识的值 void markInUse() {flags |= FLAG_IN_USE;};long when 是用来延迟执行的时间值;Handler target 是指此 msg 对应的 Handler;Runnable callback 是指 msg 对应的回调,一般默认为null,不会添加;下面重点来了,看看 Message next 及剩余的几个变量,这里牵涉到上面的问题,为什么使用 Message.obtain() 而非是 new Message()。private static final Object sPoolSync = new Object(),sPoolSync 是静态的,是所有 Message 对象共享的;我们看看 obtain() 方法,第一行代码就对 sPoolSync 使用synchronized 同步锁关键字,保持线程同步,然后对 sPool 进行判断,它也是静态,如果它值为null,则 new 一个 Message 返回;如果不为空,sPool 就是 Message 链表的 head 头部,这时候找到它的下一个,让 head 指向下一个 Message,同时切断它俩之间的链表链接,m.flags = 0 清空这个 Message 的正在使用的标识,然后返回。


我们看看 obtain() 的其它重载方法,最终都会调用到它,只不过是多了赋值。Message 是支持单链表链接的,所以才有了上面的方法,把它形成一个缓存,可以服用,上面的方法是获取缓存,那 Message 使用完毕后,是在哪里回收它形成缓存呢?我们看 recycleUnchecked() 方法。方法中是把一些成员变量制动会归零,看看同步锁里面的代码,先进行个数判断,如果 sPoolSize 小于 50,则把 sPool 赋值给当前 Message 的下一个,然后把当前 Message 变为 head 头部,同时数字 sPoolSize 加一。 如果有 Message A、B、C 三个消息,按照顺序都被调用了这个回收方法,那么调用 A 时,sPool 默认为 null,则 A 的 next 为null,sPool被赋值为 A,sPoolSize 值为1;B 调用时,B的 next 赋值为 A,sPool 赋值为 B,sPoolSize 值为2,此时链表为 B--A;C 调用回收方法,同理,则链表变为了 C--B--A。那么回收方法什么时候调用呢? Message 自身里面 recycle() 会调用, MessageQueue 消息队列中 removeMessages() 方法中会把那些删除的 Message ,也会有回收操作,Lopper 中 next() 方法
for循环结尾处,也有 msg.recycleUnchecked() 调用,说明每个 Message 只要执行完,或是等待执行但是被 remove 移除了,都会回收,这样我们使用 obtain() 方法就避免了不必要的浪费,调高效率。

接下来看看 Handler 的代码,Handler 构造方法,最终都会调用到两个方法
一:
    public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
二:    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
开头的例子中,会调用方法一,mHandler中的 Looper.myLooper() 对应的是UI线程的Looper,所使用的线程队列自然也是UI线程的队列;回忆下前面 Looper 章节中,我们在子线程中创建了 Handler,没有提前调用 Looper.prepare() 方法,那么 Handler 构造中,mLooper 就为 null,就会抛出异常了,这个之前也讲到过,这里讲的更准确些。如果我们想在子线程中创建的 Handler 仍然可以刷新UI界面,可以做到吗?看看构造方法二就知道了,我们只需要在创建 Handler 时,把UI线程对应的 Looper 穿进去即可,

    void createHandler(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler = new Handler(Looper.getMainLooper());
            }
        }).start();
    }
UI线程中的 Looper 在FrameWork层已经执行了 Looper.prepare() 和 Looper.loop(),所以我们在子线程中创建 Handler 不需要调用这两个方法了。

我们从 Looper 中知道,它从 MessageQueue 中抽取 Message,最终都调用了 Handler 的 dispatchMessage(msg) 方法,我们看看这个方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    
Message 的 callback 是 Runnable 类型,这里会先判断msg的callback对象是否为null,不会null的话就执行它,如果为null,再判断 Handler 本身的 mCallback 属性是否为null,如果不为null,看看它的回调方法返回的值是否为true,如果为true,则到此为止,如果不为true,则会执行 handleMessage(msg) 方法;如果 mCallback 它本身就是为null,也会执行 handleMessage(msg) 方法,这个方法是需要我们重写的,就像上面的例子。
Handler 发送消息有几种方法,我们先说开头例子中的 sendMessage() 方法,例子中是不延迟马上执行,如果想延迟三秒执行,则可以调用 sendMessageDelayed() 方法,看看它的源码

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    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);
    }
前面几个方法都是发送消息的,可以立刻执行,也可以延迟执行或者定时执行,都是以开机时间为标准进行操作的,最终都会走到 enqueueMessage() 方法中,在这里,会把当前 Handler赋值给 Message 的 target 属性,然后添加的 MessageQueue 队列中,这里就和上一章中往消息队列中添加消息的方法对应上了,最终会走到 Handler 的回调中。

Handler 切换子线程还有一种方法,即 post()、postAtTime()、postDelayed() 方法,和上面的 sendMessage()几个方法相对应,用 post() 举例

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    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);
    }
它经过一系列方法,最终也是走到了 enqueueMessage(queue, msg, uptimeMillis) 方法中,和上面的一样,这里注意一点,即 getPostMessage(Runnable r) 方法,在这个方法中,通过 Message.obtain() 创建了 Message 对象,然后把 runnable 对象赋值给 msg 的 callback 属性,所以通过 post() 系列方法执行的操作,最终也都到了Handler 的 dispatchMessage()方法,注意了,这时候 dispatchMessage(Message msg) 方法第一行代码 msg.callback != null 条件符合,所以就会执行 runnable 的 run() 方法,看到这也明白了,使用 Handler 的 post() 方法,仅仅是切换线程,runnable 还是在UI线程中执行,所以它里面时不能做耗时操作的,笔者刚开始学Android时就犯了这个错误,当时还不理解。

Handler 中移除消息的方法,removeCallbacks() 最终还是调用 MessageQueue 的 removeMessages() 方法,这个上一章分析过了。其他方法都是一些缓存复用方法,看看就好了,要灵活运用。Messenger 跨进程通讯时,提到使用 Messenger 发送信息,Messenger 中的 private final IMessenger mTarget 变量,对应的就是 Handler 中 MessengerImpl 对象,

class Messenger implements Parcelable {
    private final IMessenger mTarget;
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
}

class Handler {
    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
}

关于消息队列,应用层就基本讲完了,消息队列和 .aidl跨进程通讯都是 Android 的重点基础,学会他们有助于了Android架构的理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值