Handler源码分析

handler 机制是 Android 实现异步的主要方式之一,特别适用于在子线程中进行数据操作,等数据处理完成后,使用 handler 进行 UI 操控。

handler的简单使用

    /**
     * 初始化 Handler 并重写 handleMessage 方法
     */
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            Log.i("TAG", String.valueOf(msg.what));
        }
    };

        //发送 Handler 消息
        handler.sendEmptyMessage(1);

输出结果:

I/TAG: 1

分割线=================================================


基础概念

在讲源码前,先给大家输入一个基本概念:Handler 机制主要由 Handler、Message、MessageQueue、Looper 四部分组成

  • Handler :发送消息,异步回调。
  • Message:消息体。
  • MessageQueue:消息队列,存储 Handler 发送的消息。
  • Looper:循环从 MessageQueue 里面获取消息,并调用 Handler 去执行 handleMessage 方法。

源码分析(源码只保留关键部分,并非全部源码)

new Handler()分析

首先,我们先从 new Handler()开始看:

   public Handler() {
        this(null, false);
    }

其实它调用的为

    public Handler(Callback callback, boolean async) {
       ···
       //初始化 Looper
        mLooper = Looper.myLooper();
        //若 mLooper 为 null,则抛出异常
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //赋值
        mQueue = mLooper.mQueue; // final MessageQueue mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

由此我们可以看出:new Handler()的主要作用为初始化Handler的同时,也初始化Handler里面的LooperMessageQueue对象。

我们来看看mLooper是如何初始化的:Looper.myLooper();

   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

直接从sThreadLocal里面进行获取。ThreadLocal里面还有一个set方法,也就是将Looper赋值到sThreadLocal里面去,至于什么时候赋值的,我们暂时先不用管,只要知道这样new Hander()的时候,一般情况下能够正常获取得到Looper即可。

好了,new Hander()的步骤我们大致清楚了,至于重写handleMessage方法我们也可以先不用管,毕竟只是一个回调,我们先来看看handler.sendEmptyMessage(1)

sendEmptyMessage分析

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

经过重重传递后,会传递到

//将 what 封装到 msg 中
//uptimeMillis = SystemClock.uptimeMillis() + delayMillis
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    //进行 mQueue 判断
        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) {
    //重点:this 为 当前的 handler,将 handler 引用赋值到 Message 里面
        msg.target = this;
        ···
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    boolean enqueueMessage(Message msg, long when) {
		···
        synchronized (this) {
			···
			msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //将 msg 放入 MessageQueue 中
            //这里的 when 其实就是之前的 uptimeMillis = SystemClock.uptimeMillis() + delayMillis
            //在这里有个重点,就是 MessageQueue 的消息队列排序问题
            //是以 when 的值大小进行排序,从小往大进行排序
            if (p == null || when == 0 || when < p.when) {
                // 若当前传过来的 msg 的 when 最小,则将其排在头部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 根据当前 msg 的 when 大小进行插入排序
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

至此,Handler的简单使用代码我们就讲完了,其中包含了 Handler、Message、MessageQueue。等等,Looper 不见?

Looper的创建以及循环遍历

其实是因为因为在ActivityThread中的main(String[] args)已经帮我们调用了 Looper 的创建以及 Looper 的循环遍历。


    public static void main(String[] args) {
        ···
        //初始化 Looper
        Looper.prepareMainLooper();
		···
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
		···
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //循环遍历
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper.prepareMainLooper();

    public static void prepareMainLooper() {
    	//重点使用该方法进行初始化
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
   private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //这里就是之前上文所说的实例化 Looper,并 set 到 sThreadLocal 中
        //static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        //sThreadLocal的作用就是使得每个线程都有自己独立 Looper,唯一,且互不干扰
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper.loop();

    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(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
			···
            try {
            //target 就是 handler 发送消息的时候,将自身引用存储到 message 中,其实就是发送message 的 handler
            //通过 handler 去调用自身 dispatchMessage 方法
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ···
            //消息回收
            msg.recycleUnchecked();
        }
    }
    public void dispatchMessage(Message msg) {
    //优先处理 msg 里面的 callback
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        //其次处理创建 Handler 时传入的 mCallback
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后才是调用 handleMessage
            handleMessage(msg);
        }
    }

至此,Handler 的机制才是完成。
再次梳理下流程:

  • Handler 发送 Message。
  • Message 存储到 MessageQueue(以 when 的值大小进行排序)。
  • Looper 循环遍历 MessageQueue。
  • Looper 从 MessageQueue 中取出 Message。
  • Looper 调用 Message 中的 Handler 去调用 Handler 的 dispatchMessage方法。
  • dispatchMessage 方法里面包含 handleMessage 方法。

注意事项

在子线程使用 Handler 需要自己创建 Looper,并开启循环遍历

首先,我们先来一个错误的例子:

       new Thread(new Runnable() {
            @Override
            public void run() {
                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.i("TAG", "收到消息" + msg.what);
                    }
                };

                handler.sendEmptyMessage(1);

            }
        }).start();

报错

java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-16,5,main] that has not called Looper.prepare()

正确的方式如下所示:


        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.i("TAG", "收到消息" + msg.what);
                    }
                };

                handler.sendEmptyMessage(1);
                Looper.loop();
                handler.sendEmptyMessage(2);
                Log.i("TAG", "测试能否输出日志");

            }
        }).start();
I/TAG: 收到消息1

等等,这两行代码怎么没有输出?

               handler.sendEmptyMessage(2);
                Log.i("TAG", "测试能否输出日志");

那时因为在调用Looper.loop();的时候,已经开启的死循环,所以往后的代码都不会执行到了。

handleMessage所执行的线程不是创建Handler的线程,而是Looper所在的线程

这个很容易理解,因为通过上文,我们可知:
是 Looper 拿到 MessageQueue 的 Message 后,拿 Message 里面的 Handler 引用去调用 Handler 的 dispatchMessage方法,而 dispatchMessage 方法里面包含 handleMessage 方法。

简单来说

就是 Looper 在调用 handleMessage 方法。

这时,问题又来了,在最开始的 Handler 的简单使用中,并没有对于 Looper 操作,也就是在new Handler()的时候,就默认将 Handler 与所在线程的 Looper 进行绑定,很容易让人认为 Handler 与其 Looper 一定在同个线程里面。

其实,Handler 的构造方法还有这个:

    public Handler(Looper looper) {
        this(looper, null, false);
    }
    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

这时,Handler 就可以与其他线程的 Looper 进行绑定。

Handler的handleMessage里面不一定可以进行UI操作

很多人对于 Handler 的了解,都是使用 Handler 进行 UI 操作。但是,由上文可知,handleMessage 是由 Looper 调用了,也就是说,只有ActivityThreadLooper才可以进行 UI 操作。至于子线程的Looper都不行。

因此,假如我们需要在子线程使用 Handler 进行 UI 操作,可以这样做:

   Handler handler = new Handler(Looper.getMainLooper());
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值