Handler

2019-08-06  本文已影响0人  小慧sir

1、定义

一套 Android 消息传递机制

2、作用

在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理

3、意义

4、 使用方式

5、 相关概念

名称 定义 作用
UI线程 主线程(Main),用于UI更新 当数据变化,需要展示界面时,就要更新到UI线程
子线程 又叫工作线程,主要处理耗时操作 因为UI线程逻辑处理时间有限,耗时操作要放到子线程,避免ANR
Handler 线程通信的对象,线程之间任务的处理者 添加到消息队列里,接受Looper派发过来的对象
Message 消息体对象 封装要传送的对象,类似一个载体
Looper 轮训机制的对象 消息获取:不断从消息队列里拿出数据,发送给handler处理,每一个线程只能有一个Looper对象
MessageQueue 消息队列,一个数据结构(先进先出) 维护和保存发送过来的消息

那么和Handler 、 Looper 、Message、MessageQueue有啥关系?其实Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。
主线程创建一个handler对象后维护一个Looper,Looper创建一个MessageQueue,通过死循环一直检测MQ是否有消息进来,如果有通知handler处理消息,并且handler就是负责往MQ发消息的对象

6、 图解

image

7、 源码分析

  1. 新建一个handler获取到Looper对象和消息队列
new Handler()
        --->this(null, false); 
        --->Looper mLooper = Looper.myLooper();
        --->MessageQueue mQueue =  mLooper.mQueue

通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

  1. 发送消息
handler.sendMessage(message);
        --->sendMessageAtTime()
        --->enqueueMessage(queue, msg, uptimeMillis)
        --->msg.target = this;  ---  Handler对象
        --->queue.enqueueMessage(msg, uptimeMillis)  --  消息存到消息队列

  1. Looper:对于Looper主要是prepare()和loop()两个方法。

首先看prepare()方法

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例
注意:一个线程中只能有一个Looper对象,只能有一个消息队列MessageQueue

下面看Looper的构造方法:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

在构造方法中,创建了一个MessageQueue(消息队列)。

loop()方法:

final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。

        //拿到该looper实例中的mQueue(消息队列)
        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;
            }

            。。。。

            msg.target.dispatchMessage(msg);
            ---handler

        }

Looper主要作用:
1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage()去处理。

  1. handler.dispatchMessage():
        if (msg.callback != null) {
            handleCallback(msg); --- >回调自身Runnable中run方法,使用post方法的时候
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg); --- >子类必须重写
        }

        回到最后处理的方法:
            private static void handleCallback(Message message) {
                message.callback.run();
            }

            /**
             * Subclasses must implement this to receive messages.
             */
            public void handleMessage(Message msg) {
            }

使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。

4、子线程创建handler

其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行(HandlerThread)。

new Thread() {
            private Handler handler;

            public void run() {

                Looper.prepare();

                handler = new Handler() {
                    public void handleMessage(android.os.Message msg) {
                        Log.e("TAG", Thread.currentThread().getName());
                    };
                };
                Looper.loop();
            }
        }

5、 总结

  1. Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

  2. Handler的sendMessage方法,会给msg的target赋值为handler自身,然后将Message加入MessageQueue中。

  3. Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

  4. Looper.loop()会让当前线程进入一个无限循环,从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

  5. handler.dispatchMessage(msg)分发消息,如果是sendMessage(),会回调重写的handleMessage方法;如果是post(),会最后会回调 message.callback.run(),当前的run()方法。

  6. 在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

  7. 疑问:主线程死循环为什么不会卡死App?
    真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

上一篇下一篇

猜你喜欢

热点阅读