Android线程间通信Handler源码详解

目录
  • 前言
  • 01、 用法
  • 02、源码
  • 03、结语

前言

在【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、结语

图源 |《第一行代码》

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

以上就是Android线程间通信Handler源码详解的详细内容,更多关于Android线程间通信Handler的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android Handler的使用详解

    在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个"下载"按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能完成.为了保证不影响UI线程,所以我们会创建一个新的线程去执行我们的耗时的代码.当我们的耗时操作完成时,我们需要更新UI界面以告知用户操作完成了.所以我们可能会写出如下的代码: package ispring.com.testhandler; import android.app.

  • android studio的Handler简单实例代码

    实现:EditText输入消息,通过按钮选择发送给主线程或者子线程: 以下有效果图.MainActivity.java代码和activity_main.xml代码 效果图: MainActivity.java代码 package huan.san.handleroneapp; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle;

  • Android开发中Google为什么不让用Handler的runWithScissors()

    目录 一.序 二.Handler.runWithScissors() 2.1 runWithScissors() 2.2 Framework 中的使用 三.runWithScissors() 的问题 3.1 如果超时了,没有取消的逻辑 3.2 可能造成死锁 四.总结时刻 一.序 大家好,这里是承香墨影! runWithScissors() 是 Handler 的一个方法,被标记为 @hide,不允许普通开发者调用. 这个方法算是比较冷门,如果面试中被问及,面试者不知道时,通常面试官会换个问法:"

  • Android Handler使用案例详解

    什么是Handler? Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联.每个Handler的实例都关联了一个线程和线程的消息队列.当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象. handler类有两种主要用途: 执行Runnable对象,还可以设置延迟. 两个线程之间发送消息,主要用来给主线程发送消息更新UI. 为什么要用Handler 解决多线程并

  • Android Handler runWithScissors 梳理流程解析

    目录 前言 runWithScissors 梳理流程 存在的问题 总结 前言 看 WMS 代码的时候看到了 Handler.runWithScissors 方法,所以来恶补一下 public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPoli

  • Android线程间通信 Handler使用详解

    目录 前言 01.定义 02.使用 第一步.创建 第二步.发送消息 第一种是 post(Runnable) 第二种是 sendMessage(Message) 第三步.处理消息 03.结语 前言 Handler,可谓是面试题中的一个霸主了.在我<面试回忆录>中,几乎没有哪家公司,在面试的时候是不问这个问题的.简单一点,问问使用流程,内存泄漏等问题.复杂一点,纠其源码细节和底层 epoll 机制来盘你.所以其重要性,不言而喻了吧. 那么今天让我们来揭开 Handler 的神秘面纱.为了读者轻松易

  • Android线程间通信Handler源码详解

    目录 前言 01. 用法 02.源码 03.结语 前言 在[Android]线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤.那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的.本篇是关于创建源码的分析. 01. 用法 先回顾一下,在主线程和非主线程是如何创建 Handler 的. //主线程 private val mHandler: Handler = object : Handler(Looper.getMainLo

  • Android开发数据结构算法ArrayList源码详解

    目录 简介 ArrayList源码讲解 初始化 扩容 增加元素 一个元素 一堆元素 删除元素 一个元素 一堆元素 修改元素 查询元素 总结 ArrayList优点 ArrayList的缺点 简介 ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其

  • Android串口通信apk源码详解(附完整源码)

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可以用于需要使用串口通信的Android外设,该库有如下特点: 1.串口通信部分使用C++实现,在笔者接触的部分设备上实测,使用C++实现与Google官方提供的Demo的方式要快: 2.支持且必须设置串口接收最大数据长度,初始化库时填入该参数,这样设置的原因是考虑在实际使用中,规定的串口通信协议格式

  • Android实现屏幕锁定源码详解

    最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留言,我们共同学习交流,共同提高进步!直接看效果图: 一:未设置密码时进入系统设置的效果图如下: 二:设置密码方式预览: 三:密码解密效果图 四:九宫格解密时的效果图 下面来简单的看下源码吧,此处讲下,这个小DEMO也是临时学习下的,有讲的不明白的地方请朋友直接

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

  • Java8中AbstractExecutorService与FutureTask源码详解

    目录 前言 一.AbstractExecutorService 1.定义 2.submit 3.invokeAll 4.invokeAny 二.FutureTask 1.定义 2.构造方法 3.get 4.run/ runAndReset 5. cancel 三.ExecutorCompletionService 1.定义 2.submit 3.take/ poll 总结 前言 本篇博客重点讲解ThreadPoolExecutor的三个基础设施类AbstractExecutorService.F

  • python目标检测SSD算法训练部分源码详解

    目录 学习前言 讲解构架 模型训练的流程 1.设置参数 2.读取数据集 3.建立ssd网络. 4.预处理数据集 5.框的编码 6.计算loss值 7.训练模型并保存 开始训练 学习前言 ……又看了很久的SSD算法,今天讲解一下训练部分的代码.预测部分的代码可以参照https://blog.csdn.net/weixin_44791964/article/details/102496765 讲解构架 本次教程的讲解主要是对训练部分的代码进行讲解,该部分讲解主要是对训练函数的执行过程与执行思路进行详

  • Android 带logo的二维码详解及实例

    Android 带logo的二维码详解及实例 好久没有写博客了,快元旦了公司的事情也不是很多,刚好和朋友一起出去玩玩,朋友是搞PHP的说到了每天在公司都是搞些什么二维码和微信支付的相关东西,因为上班的时间不忙,所以随便来搞下. 二维码(Quick Response Code),又称二维条码,它是用特定的几何图形按一定规律在平面(二维方向)上分布的黑白相间的图形,是所有信息数据的一把钥匙.在现代商业活动中,如果一个产品是不能通过二维码来进行访问什么的,显然是不成功的.用的比较多的生成二维码的jar

  • Spring AOP底层源码详解

    ProxyFactory的工作原理 ProxyFactory是一个代理对象生产工厂,在生成代理对象之前需要对代理工厂进行配置.ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术. // config就是ProxyFactory对象 // optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface if (config.isOptimize() || config.isProxy

随机推荐