编程知识 cdmana.com

Android Handler深度解析源码(一)

Hander 简单使用

handler机制是线程间进行通信的一种简单方式,在Android中常用于在子线程中请求网络数据,然后利用handler通知ui线程进行ui渲染。它的通信是单向的,只能一个线程进行接收,另外一个线程进行发送。下面的2个例子分别是从子线程给主线程发送消息和从主线程给子线程发消息

	 /**
     * 子线程往主线程发消息
     */
    private fun work2UiHandler()
    {
   
   
        //注意 Handler的无参构造函数已经在 SDK 30版本已经被废弃了
        //用户需要显性的提供当前线程的Looper,这里我们提供的主线程的Looper
        val handler = object: Handler(Looper.getMainLooper())
        {
   
   
            override fun handleMessage(msg: Message)
            {
   
   
                when(msg.what)
                {
   
   
                    10001->Log.i("mainActivity","主线程更新 ---${ msg.obj}")
                }
            }
        }
        Thread{
   
   
            //子线程进行耗时操作
            //这里得Message建议不要用new的方式去获取,因为Message内部自己维护了一个缓存的消息池
            //当message 被处理完毕后,系统会自动回收,
            // 如果是自己new的Message,每次使用后,系统会放入到消息池中,
            //没有进行一个回收的过程,会占用内存,后面会有源码分析该消息缓存机制
            val message = Message.obtain()
            message.what = 10001
            message.obj = "这是子线程耗时获取的数据"
            //通知主线程线程进行UI更新
            handler.sendMessage(message)
        }.start()
    }
 /**
     * 从主线程给子线程发消息
     */
    private fun ui2WorkHandler()
    {
   
   
        val workThread = WorkThread()
        workThread.start()
        //等待工作的线程的run方法执行结束,即handler初始化完成
        Thread.sleep(1000)
        //通过handler获取message 其内部最后还是调用的Message.obtainMessage()
        val message = workThread.mHandler.obtainMessage()
        message.what = 10002
        message.obj = "这是主线程发送的数据"
        workThread.mHandler.sendMessage(message)
    }

    class WorkThread:Thread()
    {
   
   
        lateinit var mHandler:Handler

        override fun run() {
   
   

            //为当前的工作线程初始化looper
            Looper.prepare()
            //初始化工作线程的的handler Looper.myLooper获取的即为与当前线程绑定的looper
            mHandler = object : Handler(Looper.myLooper()!!)
            {
   
   
                override fun handleMessage(msg: Message) {
   
   
                    when(msg.what)
                    {
   
   
                        10002->Log.i("mainActivity","子线程收到消息 ---${ msg.obj}")
                    }
                }
            }
            //looper的任务消息队列开始轮询执行
            //后续源码会详细分析
            Looper.loop()
        }
    }

源码分析

关于下面四个类之间的关系

Handler Looper MessageQueue Message.obtainMessage() sendMesssage() message存入队列 Looper.loop() 轮询队列 取出message handlerMessager() Handler Looper MessageQueue

从上面的关系可以看出,Looper是Handler和MessageQueue中间桥梁,数据载体是Message, MessageQueue存储Handler发送过来的Message。Handler会持有Looper,Looper会持有MessageQueue

Looper

public class Looper
{
   
   
    //每一个线程 都会关联一个ThreadLocalMap<ThreadLocal,Looper> 该map中存储的是ThreadLocal-->Looper的键值对
    //即 一个thread 对应 一个 ThreadLocalMap,但是map中存储的ThreadLocal均为同一个,Looper就不同
    //总结即 一个thread 至多只能绑定一个Looper(也可为null,即当前线程未绑定Looper,需调用Looper.perpare()进行线程绑定)
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //主线程绑定的Looper
    private static Looper sMainLooper;  // guarded by Looper.class

    //Looper与之关联的消息队列
    final MessageQueue mQueue;

    //针对消息分发的前后回调通知
    private static Observer sObserver;

    //这里可以进行一个全局的设置,去回调监听消息分发的全过程
    //但是这个方法被影藏了,如果有需要可以进行Hook二次代理即可
    @hide
    public static void setObserver(@Nullable Observer observer) {
   
   
        sObserver = observer;
    }

    private static void prepare(boolean quitAllowed) {
   
   
        //调用该方法时,会判断当前的线程是否已经绑定了Looper,
        //sThreadLocal.get()不为null则为当前线程是已经绑定了一个Looper,此时会抛出异常
        if (sThreadLocal.get() != null) {
   
   
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //实例化Looper并将其与当前线程进行绑定,即将looper存入与线程想关联的ThreadLocalMap中
        //具体源码本章节不做过多解析
        sThreadLocal.set(new Looper(quitAllowed));
    }

    public static @Nullable Looper myLooper() {
   
   
        //获取当前线程绑定的looper,可能为null,为null则为当前线程未绑定looper
        return sThreadLocal.get();
    }

    //刚方法会在ActivityThread的main方法中被调用,即为主线程绑定了Looper,开发日常所有回调到Ui线程的looper
    //都是 sMainLooper
    public static void prepareMainLooper() {
   
   
        prepare(false);
        synchronized (Looper.class) {
   
   
            if (sMainLooper != null) {
   
   
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

     //开始轮询消息队列,处理消息
     public static void loop() {
   
   
        //获取当前的线程的looper
        final Looper me = myLooper();
        ...
        //获取looper所关联的messageQueue
        final MessageQueue queue = me.mQueue;
        ...
        //轮询队列
        for (;;) {
   
   
            //取出消息,当无消息时,该线程会处于一个阻塞的状态
            Message msg = queue.next(); // might block
            ...
             if (observer != null) {
   
   
                //分发消息开始
                token = observer.messageDispatchStarting();
            }
            ...
            try {
   
   
                //msg.target所代表的目标是 handler,分析Message时会讲到
                //这里则是handler分发处理消息起始点
                msg.target.dispatchMessage(msg);
                //分发消息结束
                observer.messageDispatched(token, msg);
            } catch (Exception exception) {
   
   
                //分发的当次消息抛出了异常
                observer.dispatchingThrewException(token, msg, exception);
            }
            ...
            //回收消息
            msg.recycleUnchecked();
        }
    }
}

Message

public class Message
{
   
   
    //代表Message的标识
    public int what;

    public int arg1;

    public int arg2;

    public Object obj;

    //handler发送消息时,会与message进行一个绑定,该值即为当前的handler
    //这个target很重要
    Handler target;

    //可赋值,代表Message被分发处理时,会被执行的一个任务
    Runnable callback;

    //Message在链表结构中的的下一个节点
    //该缓存池是以单链表的方式存储的
    Message next;

    //Message缓存池(单链表的头指针)
    private static Message sPool;

    //缓存池的初始大小
    private static int sPoolSize = 0;

    //缓存池的最大容量
    private static final int MAX_POOL_SIZE = 50;

    public static Message obtain() {
   
   
        synchronized (sPoolSync) {
   
   
            //初始缓存池为null,会直接new Message返回实例对象
            if (sPool != null) {
   
   
                //当该链表的头指针不为null,即当前缓存池中已经有了可以重新回收的message对象
                //这里取出头指针
                Message m = sPool;
                //将当前的头指针指向下一个节点
                sPool = m.next;
                //取出的头指针的next置null
                m.next = null;
                //size-1
                sPoolSize--;
                //将头指针返回出去,即从缓存池中取出了一个已经回收的Message对象
                return m;
            }
        }
        return new Message();
    }

    void recycleUnchecked() {
   
   
        //清除Message携带的所有数据
        ...
        synchronized (sPoolSync) {
   
   
            //如果当前的缓存池大小 未达到最大容量,会将该回收的message置于缓存池中(单链表)
            //头插法
            if (sPoolSize < MAX_POOL_SIZE) {
   
   
                //尾指针指向下个节点
                next = sPool;
                //当前节点为头指针
                sPool = this;
                //size+1
                sPoolSize++;
                //当下一个Message(这里指 msgB对象)又被回收后,msgB的next指向上一个节点,头指针指向当前的msgB对象
                //以此类推,后面每一个被回收的message都会被插入到链表的最前端
            }
        }
    }

    //利用message绑定的handler直接去发送消息
    public void sendToTarget() {
   
   
        target.sendMessage(this);
    }

    //这里也是获取一个缓存池中的Message,只不多了handler和callback
    //这么做的目的是直接将两者绑定到message中去,往往在获取到该Message后可以直接去调用sendToTarget()发送消息
    public static Message obtain(Handler h, Runnable callback) {
   
   
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

}

Handler

public class Handler
{
   
   
    //与handler绑定的looper,一一对应
    final Looper mLooper;

    //looper中的消息队列
    final MessageQueue mQueue;

    //回调函数
    final Callback mCallback;


    //没有传入looper时,会调用Looper.myLooper()获取与当前线程绑定的looper
    public Handler(@Nullable Callback callback, boolean async) {
   
   
        ...
        mLooper = Looper.myLooper();
        ...
        //将looper中消息队列赋值
        mQueue = mLooper.mQueue;
        //handler的回调函数,在dispatchMessage()分发消息方法中会被调用
        mCallback = callback;
        mAsynchronous = async;
    }


    //通过Runnable 获取message 将runnable任务与message绑定,最后也会在dispatchMessage()分发消息方法中会被调用
    private static Message getPostMessage(Runnable r) {
   
   
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    //所有的post的方法 最后都会走到sendMessageAtTime
    public final boolean post(@NonNull Runnable r) {
   
   
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
   
   
        if (delayMillis < 0) {
   
   
            delayMillis = 0;
        }
        //SystemClock.uptimeMillis() + delayMillis 是否延迟执行 即在定点的时间上去执行
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
   
   
        MessageQueue queue = mQueue;
        ...
        //将消息放入消息队列中
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
   
   
        //将msg的target与当前handler进行一个一对一绑定
        msg.target = this;
        ...
        //入队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    //handler的回调函数
    public interface Callback {
   
   
        boolean handleMessage(@NonNull Message msg);
    }

    //handler的处理消息逻辑,用户需要自己重写
    public void handleMessage(@NonNull Message msg) {
   
   
    }

    //该方法很重要
    public void dispatchMessage(@NonNull Message msg) {
   
   
        //该方法在Looper.loop()中轮询队列时,取出消息后
        // message.target.dispatchMessage(message) 执行
        //这里就是Message的callback不为null时会执行处理消息
        if (msg.callback != null) {
   
   
            handleCallback(msg);
        } else {
   
   
            //当Message的callback不去消费处理消息
            //handler自身的回调函数会首先去处理消息
            if (mCallback != null) {
   
   
                //如果handler的mCallBack.handleMessage方法去消费处理消息时
                //返回的是true则handler自身的handleMessage方法就不会被执行
                //返回的是false是,就还会去执行handleMessage
                if (mCallback.handleMessage(msg)) {
   
   
                    return;
                }
            }
            handleMessage(msg);
            //总结即:Message的callback最先被执行,然后才是handler自身的CallBack回调函数执行
            //而handler的handleMessage方法则需要看CallBack中函数返回值来决定是否执行
        }
    }
}

MessageQueue

/**
* 负责管理消息队列,实际上Message类有一个next字段,
* 会将Message对象串在一起成为一个消息队列,
* 所以并不需要LinkedList之类的数据结构将Message对象组在一起成为队列。
*/
public class MessageQueue
{
   
   
    //native层需要用的句柄
    private long mPtr;

   //native层去执行一个消息队列的初始化
    private native static long nativeInit();

    //该过程是一个阻塞的操作
    private native void nativePollOnce(long ptr, int timeoutMillis);

    MessageQueue(boolean quitAllowed) {
   
   
        //允许退出
        mQuitAllowed = quitAllowed;
        //初始化获取句柄
        mPtr = nativeInit();
    }

    /**
    *
    * 用于获取下一个Message对象,如果没有需要处理的Message对象,该方法将阻塞。
    * MessageQueue用本地方法做同步互斥,因为这样时间更精准。
    * 每个Message对象都有一个什么时刻处理该Message对象的属性when,
    * 没到时间都不会处理该Message对象,如果时间不精准的话,会导致系统消息不能及时处理。
    */
    Message next() {
   
   
        //具体代码不做过多分析,后续会出一个新章节来专门分析 MessageQueue的实现原理
       ...
    }

    /**
    * 队列中的Message触发时间是有先后顺序的。
    * 当消息加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,
    * 以保证所有消息的时间顺序(内部遍历队列中Message,找到when比当前Message的when大的Message,
    * 将Message插入到该Message之前,如果没找到则将Message插入到队列最后)。
    * 一般是当前队列为空的情况下,
    * next那边会进入睡眠,需要的时候MessageQueue这边会唤醒next方法。
    */
    boolean enqueueMessage(Message msg, long when)
    {
   
   
      //具体代码不做过多分析,后续会出一个新章节来专门分析 MessageQueue的实现原理
        ...
    }
}

Handler的内存泄露

  private val mHandler = object :Handler(Looper.getMainLooper()){
   
   
        override fun handleMessage(msg: Message) {
   
   
            Log.i("HandlerLinkActivity","处理消息")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?)
    {
   
   
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //延时2分钟发送一个空消息
        mHandler.sendEmptyMessageDelayed(100086,2*60*1000)
    }

这里在Activity中延迟2分钟去发送一个消息,然后再这两分钟内,按返回键去关闭Activity此时就会存在一个内存泄露的现象,造成内存泄露的原因时,mHandler是一个匿名的内部类,它会持有一个外部内的引用即当前Activity的引用,当gc时,发现Activity还在被引用时,就无法去回收内存

解决方法:

 /**
     * 避免handle内存泄露的办法
     * 1.使用static 修饰的handler,但是一般会弱引用activity对象,因为要使用activity对象中的成员
     * 2.单独定义handler,同样可以弱引用activity
     * 3.使用内部类的handler,在onDestroy方法中removeCallbacksAndMessages
     */
    class StaticHandler(activity: HandlerLinkActivity) :Handler(Looper.getMainLooper())
    {
   
   
        var mWeakReference: WeakReference<HandlerLinkActivity> = WeakReference(activity)


        override fun handleMessage(msg: Message) {
   
   
            mWeakReference.get().run {
   
   
                //执行更新ui
            }
        }
    }

版权声明
本文为[osc_97wmavr6]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4400708/blog/4816260

Tags Android
Scroll to Top