Handler消息机制笔记_handler发送消息的方法

Handler消息机制笔记_handler发送消息的方法

编码文章call10242025-02-27 12:31:5224A+A-

以下是我看了何红辉大神的《Android开发进阶——从小工到专家》和主席的《Android开发艺术探索》关于Handler做的一些笔记和总结:

为什么只允许在主线程中更新UI?网络等耗时操作要放在子线程?

因为多个线程并发操作UI,可能会产生线程不安全现象,因此规定只能在主线程中更新UI。而网络请求等操作如果放在主线程,考虑到网速等原因,可能会造成ANR(程序未响应),导致主线程被阻塞,所以这类耗时操作应该放在子线程。

Handler消息机制处理流程:

UI线程:即主线程,系统在创建主线程的时候,会在主线程中初始化一个Looper对象,同时也会创建一个相应的MessageQueue消息队列。

MessageQueue:虽然叫消息队列,但是它的内部实现并不是用的队列,实际上它是用单链表的数据结构来存储消息列表。

Handler:发送和处理消息。如果要Handler正常工作,在当前线程中必须要有一个Looper对象。

Looper:每个线程只能有一个Looper对象,在主线程被创建的时候,会在主线程中初始化一个Looper对象,调用Looper的loop方法后,会陷入一个无限的循环中——每当发现MessageQueue中存在一条消息,就会将它取出,传递到Handler的handleMessage方法中。

主线程与Handler、Looper、MessageQueue、ThreadLocal的关系?

我们都知道主线程在创建时,会自动初始化一个Looper对象,并且会创建一个消息队列,同时开启消息循环。主线程的这一系列操作是在ActivityThread.main方法中创建的,该函数是Android应用程序的入口,我们现在来看源代码:

Looper.prepareMainLooper;//1.创建消息循环Looper ActivityThread thread = new ActivityThread; thread.attach(false); if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler; } if (false) { Looper.myLooper.setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop;//2.执行消息循环 throw new RuntimeException("Main thread loop unexpectedly exited");

从源代码可以看出,主线程通过调用prepareMainLooper方法来创建Looper,然后执行loop方法来开启消息循环。

我们再来看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"); } sThreadLocal.set(new Looper(quitAllowed)); }
public static Looper myLooper {
returnsThreadLocal.get;
}

prepareMainLooper方法中又调用了prepare方法,而prepare方法最后使用了

sThreadLocal.set(new Looper);创建了Looper对象,以及将Looper对象封装到了ThreadLocal线程中。

而消息队列MessageQueue封装在Looper中,Looper的构造函数中会创建一个MessageQueue消息队列。

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

接下来我们来看Handler是如何跟Looper、MessageQueue关联起来的,看Handler源代码中的构造方法。

public Handler(Callback callback, boolean async) {
   //代码省略
    ......
    mLooper = Looper.myLooper;//获取Looper
    if (mLooper == null) {
        throw new RuntimeException(
 "Can't create handler inside thread that has not called Looper.prepare");
    }
    mQueue = mLooper.mQueue;//获取消息队列
    mCallback = callback;
    mAsynchronous = async;
}

Handler通过调用Looper.myLooper方法来获取Looper对象,通过mLooper.mQueue来获取消息队列,这样当Handler写在主线程中,就跟Looper、MessageQue有关系了。

我们再来看主线程ActivityThread后面又执行的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; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); //处理消息 //代码省略 msg.recycleUnchecked; } }

从源代码中可以看出,其实loop方法就是建立了一个死循环即消息循环,不断的从消息队列中取除消息,然后交给dispatchMessage方法处理消息,而target是Handler对象,因此dispatchMessage是Handler中的方法。

我们再来看dispatchMessage方法是如何处理消息的。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
 if (mCallback.handleMessage(msg)) {
 return;
 }
        }
        handleMessage(msg);
    }
}

dispatchMessage方法实际上就是通过判断callback,当我们使用Handler来sendMessage时通常不会设置callback的值,因此,就相当于再调用主线程中Handler的handleMessage方法。

这样看有点头晕,为了能够更好的理清之间的关系,我又做了关系图,整个过程就是:

处理过程:首先要在主线程中创建一个Handler对象,并重写handleMessage方法,然后当子线程需要进行UI操作时,就会创建一个Message对象,并通过Handler将消息发送出去。之后这条消息就被添加到MessageQueue消息队列中,Looper通过执行loop方法会建立一个死循环即消息循环,通过queue.next方法不断从MessageQueue中取出消息,再调用Handler的dispatchMessage方法处理消息,即Handler的handleMessage方法。

Handler写在主线程中:

由于主线程在创建时,会在主线程中初始化一个Looper对象,所以只要创建Handler对象。

Handler写在子线程中:

我们就要自己创建一个Looper对象

①调用Looper.prepare方法即可为当前线程创建一个Looper对象,并会配置相应的MessageQueue。

②在子线程中创建Handler对象,重写handleMessage方法。

③调用Looper.loop方法启动Looper循环,不断从MessageQueue中取出消息。

④最后注意该子线程必须在主线程中启动,new xxThread.start;

class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run {
* Looper.prepare;
*
* mHandler = new Handler {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop;
*      }
*  }

Handler是如何获取到当前线程的Looper呢?

通过ThreadLocal,ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。

为什么更新UI的Handler必须要在主线程中创建?

因为Handler要与主线程的消息队列关联上,这样handleMessage才会执行在UI线程中,此时更新UI才是线程安全的。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4