软件编程
位置:首页>> 软件编程>> Android编程>> Android线程间通信Handler源码详解

Android线程间通信Handler源码详解

作者:Quincy_Ye  发布时间:2021-12-31 07:04:10 

标签:Android,线程,通信,Handler

前言

在【Android】线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤。那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的。本篇是关于创建源码的分析。

01、 用法

先回顾一下,在主线程和非主线程是如何创建 Handler 的。

//主线程
private val mHandler: Handler = object : Handler(Looper.getMainLooper()) {
   override fun handleMessage(msg: Message) {
       when (msg.what) {
           1 -> {}
       }
   }
}
//子线程
Thread {
   Looper.prepare()
   val handler = object : Handler() {
       override fun handleMessage(msg: Message) {
           when (msg.what) {
               1 -> {}
           }
       }
   }
   handler.sendEmptyMessage(1)
   Looper.loop()
}.start()

02、源码

Handler 一共有 7 个构造方法。但最后都会直接或间接使用到以下两个构造方法。所以我们看看两个方法都做了什么事情吧。

//方法 1
//Handler.java
public Handler(@Nullable Callback callback, boolean async) {
   if (FIND_POTENTIAL_LEAKS) {
       final Class<? extends Handler> klass = getClass();
       if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
               (klass.getModifiers() & Modifier.STATIC) == 0) {
           Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
               klass.getCanonicalName());
       }
   }
   mLooper = Looper.myLooper(); \\标识 1
   if (mLooper == null) { \\ 标识 3
       throw new RuntimeException(
           "Can't create handler inside thread " + Thread.currentThread()
                   + " that has not called Looper.prepare()");
   }
   mQueue = mLooper.mQueue; \\标识 2
   mCallback = callback;
   mAsynchronous = async;
}
//方法 2
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
   mLooper = looper;
   mQueue = looper.mQueue;
   mCallback = callback;
   mAsynchronous = async;
}

方法 2 更加的简单,那么我们就从它入手吧。在其中对 4 个变量进行赋值。分别是 mLooper, mQueue, mCallback, mAsynchronous。方法 1 主要也是对 4 个变量进行赋值。它们有什么作用,我们先不管,后面会讲到。我们先来看看这方法 1 和方法 2 的区别是什么?

让我们聚焦到标识 1 和标识 2,mLooper, mQueue 来源是通过 Looper 这个类中获取的。那让我们跟进去看看。

//跟进标识 1
//Looper.java
public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

看到是通过 mThreadLocal.get() 获得一个 Looper 实例。那么 mThreadLocal 的 Looper 又是哪里来的呢?让我们找找 mThreadLocal.set() 方法,就知道了!

//Looper.java
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));
}

原来是在 Looper.prepare() 中添加的。

这里需要注意!

  • "Only one Looper may be created per thread",每个线程只能调用一次 prepare(),这也就意味着每个线程只有一个 Looper 。

可是看回在主线程中创建 Handler 的时候,我们并没有调用 Looper.prepare() 方法。但是也正常运行了。那是为什么呢?那是因为在 ActivityThread 中的 main()已经调用了。

//ActivityThread.java
public static void main(String[] args) {
   //...省略无关代码
   Looper.prepareMainLooper(); \\标识 4
  //...
   ActivityThread thread = new ActivityThread();
   thread.attach(false, startSeq);
   if (sMainThreadHandler == null) {
       sMainThreadHandler = thread.getHandler();
   }
 //...
   Looper.loop();
}
//跟进标识 4
//Looper.java
public static void prepareMainLooper() {
   prepare(false);
   synchronized (Looper.class) {
       if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
       }
       sMainLooper = myLooper();
   }
}

这里需要注意!

  • 我们不仅不用调用,也不能调用。否则将会触发 IllegalStateException("The main Looper has already been prepared.") 异常。

  • 但是如果不是为主线程创建 Handler 的时候,我们就需要手动调用 Looper.prepare(), 否则该线程中的 Looper 为空,会触发标识 3 的代码块。

  • 即会得到RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()")异常。

03、结语

Android线程间通信Handler源码详解

图源 |《第一行代码》

最后结合《第一行代码》中异步消息处理机制的流程图。我们可以看出 Handler 创建过程主要是准备好了 Looper, MessageQueue 和 Handler 本身。

来源:https://juejin.cn/post/7155791766631743524

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com