Handler Looper MessageQueue的应用

1 概述

前面几篇介绍了Handler、Looper、MessageQueue的源码实现原理,理解了原理,下一步我们要知道的就是怎么用好这些内容.

2 获取Message对象

首先,不建议使用new Message方法,而是使用Message.obtain()方法,因为obtain()方法内部有一套消息池机制,首先从消息池获取可用消息,没有的时候才新建消息,这样就避免创建多余的消息对象造成内存浪费。
获取Message的方法:
1 Messgae.obtain() 优先从消息池中获取消息,没有空闲消息再创建。
2 Message.obtain(Message msg) 获取一个msg消息的拷贝
3 handler.obtain() 获取消息的同时指定这个消息的target为调用Handler

3 主线程中使用

1、主线程中默认就已经初始化一个Looper,故可以直接new Handler()默认的空构造方法生产一个主线程的Handler,然后通过handler的sendMessage或post方法,完成消息的发送,再通过复写handler的 handlerMessage方法处理消息的回调。

2、有一点需要注意的是,由于sendMessage的方法处理,或者是post的Runnable,最终都是在主线程,也就是UI线程中执行的,故不要放耗时操作,避免ANR

3 子线程中获取主线程的Looper和MessageQueue

Android的Looper默认提供了一个静态方法Looper.getMainLooper(),用于获取主线程的Looper对象。其实现原理,前面已经提过,主线程的looper会默认存储在全局变量sMainLooper中,通过该方法获取到。拿到looper对象后,就可以通过looper.getQueue()获取该Looper的MessageQueue,通过new Handler(looper),获取主线程的handler了。此时,拿到主线程的handler,就可以发处理UI的消息给主线程执行了。

4子线程中使用Looper

首先需要初始化一个looper

class MyLooperThread extends Thread{
    public void Handler handler;
    public void run(){
        Looper.prepare();
        handler = new Handler(){
            public void handlerMessage(Message msg){
                //...处理消息回调,子类实现具体业务
            }
        };
        Looper.loop();//这里进入循环
    }
}

初始化完成后,需要再次获取此looper,则通过Looper.myLooper(),获取当前线程的looper。可以通过Looper.myQueue()方法用于获取当前线程的Looper的MessageQueue。此时也可以通过new handler()创建一个handler(默认使用Looper.myLooper()取到的looper,也就是子线程的looper),此handler是子线程的handler,而不是UI线程的Handler,不能用此handler来更新UI

5 runOnUiThread

这个方法位于activity类中,实现也是通过主线程的handler,post一个Runnable给主线程的handler执行。所以同样,此方法传递的Runnable中也不要有耗时操作。

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

6 消息的取消、查询和发送

sendMessage发送的消息通过removeMessage(int what)来取消
post发送的消息,通过removeCallback(Runnable action)来取消

hasMessages(int what) 查询当前是否含有what的消息
hasCallbacks(Runnable r) 查询当前是否有r这个Runnable

postAtFrontOfQueue 把消息/Runnable插入到队列头部

7Handler可能引起的内存泄露

Handler作为内部类的时候,由于内部类会持有外部类对象的一个引用,故此Handler可能导致外部类的实例对象无法被回收。此时,外部若是一个activity,则其onDestory方法一直不会被调用。

解决方式一:设置该Handler为static,静态方法或者属性为类的方法或属性,而不是对象的方法或者属性,故不会持有外部类的对象的引用,也就不会导致外部对象无法被回收。

解决方式二:使用弱引用实现,定义一个工具类,封装其若引用。

public class HandlerUtil {
    private static int mId = 0x1000000;

    public interface MessageListener {
        public void handleMessage(Message msg);
    }

    public static final int generateId() {
        return ++ mId;
    }

    public static class StaticHandler extends Handler {
        WeakReference<MessageListener> listener;

        public StaticHandler(MessageListener listener) {
            super();
            this.listener = new WeakReference<MessageListener>(listener);
        }

        public StaticHandler(Looper looper, MessageListener listener){
            super(looper);
            this.listener = new WeakReference<MessageListener>(listener);
        }

        public StaticHandler() {
            super();
        }

        @Override
        public void handleMessage(Message msg) {
            MessageListener listener = this.listener.get();
            if (listener != null) {
                listener.handleMessage(msg);
            }
        }
    }
}

说明:

    1. 通过generateId()方法生成一个唯一的ID,App全局唯一,这样可以防止可能发生的消息ID重复而导致的各种问题。
    1. 定义一个StaticHandler静态Handler,继承原Handler接口, 用来消除Handle可能导致的泄漏。
    1. StaticHandler中含有一个弱引用WeakReference<MessageListener> listener,在构造的时候传入此弱引用的值。此listener必须由Activity实现该接口(推荐)或者是宿主Activity的类成员,因为弱引用不会增加引用计数,若是使用匿名变量则会导致listener过早释放
    1. 最终调用到Handler的handlerMessage处理消息,在这个方法内部,通过this.listener.get() 获取listener的强引用,获取成功的时候再回调其handlerMessage方法,也就最终实现了消息的处理。

8 HandlerThread

android提供了一个HandlerThread方法,简单封装了子线程实现handler looper机制,方便上层使用。此HandlerThread的使用方法和主线程的使用方法类似,只是不能从通过它更新UI。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值