美文网首页
带着问题看 Handler

带着问题看 Handler

作者: 028257ecd619 | 来源:发表于2018-12-07 17:43 被阅读25次

Q:简述 Android 消息机制原理

  • 首先,创建和线程绑定的 Looper 对象,同时会创建一个与之关联的 MessageQuue 对象用于消息的存储
  • 然后,开启 Looper 的消息循环,从 MessageQueue 中获取待处理的消息,无消息则阻塞线程
  • 通过 Handler 发送消息,会将消息添加到 MessageQueue 中,同时 Looper 被唤醒处理消息
  • Looper 获取到消息,将消息投递到 Message 对象对应的 Handler 进行处理

Android 的消息机制主要是指 Handler 的运行机制,Handler 的运行需要底层的 Looper 和 MessageQueue 支持,MessageQueue 可以理解为消息队列,用来存储 Handler 发出的消息,而 Looper 可以理解为消息循环,由于 MessageQueue 只用来存储消息,不能处理消息,所以 Looper 就是用来处理消息的,Looper 会以无限循环的形式去消息队列中获取新消息,如果有的话就处理消息,没有就一直等待着。而 Looper 还涉及到一个概念就是 ThreadLocal,ThreadLocal 可以让数据在不同线程中实现不同的数据副本,当 Handler 创建的时候,会获取当前线程的 Looper 来构建消息循环系统,而这就是通过 ThreadLocal 来获取的。需要注意的是,线程默认是没有 Looper 的,要使用 Handler 就必须为线程创建 Looper,而我们可以在主线程中直接创建 Handler,是因为主线程在创建的时候就初始化了 Looper 并开启了消息循环。

Q:为什么在主线程可以直接创建 Handler?

public static void main(){
    // 1: 创建主线程 Looper 对象
    Looper.prepareMainLooper();
 
    // 2: 开始循环
    Loop.loop();
}

Android 的主线程为 ActivityThread,在入口函数 main 里面已经初始化了 Looper 对象并调用 Looper.loop() 方法开启了消息循环。

Q:Looper 是如何绑定 MessageQueue 的?

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

在 new Looper 的时候,会在构造方法里创建 MessageQueue 对象并赋值给成员变量 mQueue。

Q:Looper.loop() 方法是如何工作的?

public static void loop() {
    // step1: 获取当前线程的 Looper 对象
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException(
            "No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // step2: 获取 Looper 保存的 MessageQueue 对象
    final MessageQueue queue = me.mQueue;
    ...
    // step3: 循环读取消息,如果有则调用消息对象中储存的 handler 进行发送
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        try {
            // step4: 使用 Message 对象保存的 handler 对象处理消息
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        msg.recycleUnchecked();
    }
}
  1. 获取当前线程中的 Looper 对象,如为空,则抛异常
  2. 获取 Looper 对象保存的 MessageQueue 对象
  3. 开启循环读取消息,调用 MessageQueue 对象的 next() 方法获取消息,如果没有消息,就会进行阻塞,如果有消息,则使用 Message 对象保存的 Handler 对象来处理消息,调用 Handler 对象的 dispatchMessage 方法
  4. 处理完成释放消息对象

Q:消息分发的优先级是怎样的?

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
  • Message 的回调方法 callback 优先级最高
  • Handler 的回调方法 mCallback.handlerMessage 优先级第二
  • Handler 的空方法 handlerMessage 优先级最低

Q:Handler 如何往 MessageQueue 中插入消息,Message 是如何绑定 Handler 的?

final MessageQueue mQueue;
 
public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
// 发送延时消息
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);
}
// 处理消息,赋值 Message 对象的 target,消息队列插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

调用 Handler 的 sendMessage 方法,最终会调用 enqueueMessage 方法,在这个方法里,会将 Handler 赋值给 msg.target,这样两者之间就产生了联系,最后,会调用 MessageQueue 的 enqueueMessage 方法来插入消息。

Q:Handler 如何绑定 MessageQueue 的?

public Handler(Callback callback, boolean async) {
    // step1:获取当前线程 Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called         Looper.prepare()");
    }
 
    // step2:获取 Looper 对象绑定的 MessageQueue 对象并赋值给 Handler 的 mQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler 在创建的时候会通过 ThreadLocal 取出当前线程存储的 Looper 对象,并且将 Looper 对象的 MessageQueue 赋值给自身的成员变量 mQueue。

Q:在任何地方下创建 Handler,那么 Handler 是处于什么线程?

public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
// 第一个参数是 looper 对象,第二个 callback 对象,第三个消息处理方式(是否异步)
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
  • 不传递 Looper 对象创建 Handler,那么就会默认获取当前线程的 Looper 对象,那么 Handler 的操作就都是在当前线程执行的
  • 传递 Looper 对象创建 Handler,那么传递的 Looper 对象是哪个线程的,Handler 的操作就在哪个线程执行

Q:子线程怎么创建 Handler?

new Thread() {
    @Override
    public void run() {
        // step1
        Looper.prepare();
         // step2
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == 1){
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            // 执行操作
                        }
                    });
                     // step5
                    Looper.myLooper().quit();
                }
            }
        };
         // step3
        handler.sendEmptyMessage(1);
         // step4
        Looper.loop();
    }
}.start();

Q:ThreadLocal 的原理是什么?

ThreadLocal 可以实现数据在不同线程下拥有不同的副本,互不干扰。

public class Thread implements Runnable {
    ThreadLocal.Values localValues;
}
 
public class ThreadLocal<T> {
    Values values(Thread current) {
        return current.localValues;
    }
    
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
 
    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }
        return (T) values.getAfterMiss(this);
    }
}

Thread 类内部有一个成员变量 ThreadLocal.Values localValues 专门用来存储 ThreadLocal 的数据。

从 ThreadLocal 的 set 和 get 方法可以看出,他们所操作的对象都是当前线程的 localValues 对象中的数组,因此在不同线程中访问同一个 ThreadLocal 的 set 和 get 方法所做的读写操作仅限于各自线程内部,所以 ThreadLocal 可以在多个线程中互不干扰的存储和修改数据。

相关文章

网友评论

      本文标题:带着问题看 Handler

      本文链接:https://www.haomeiwen.com/subject/lnhxhqtx.html