Android的Handler

ops/2024/11/8 21:46:38/

1. Handler是用于线程间通信,本质上是:

Handler调用发送方法,向与Looper绑定的消息队列写入消息,然后Looper.loop()会循环的从消息队列里拿出消息。并调用dispatchMessage处理消息。而需要此消息的线程会实现回调的handleMessage接口来处理消息。

2.举个例子:主线程调用子线程的Handler发送消息。

package com.android.car.myapplication;import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private TextView textView;private Handler mainHandler;private Handler backgroundHandler;private Thread backgroundThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_carsetting);textView = findViewById(R.id.textView);// 主线程的 Handler,用于更新 UImainHandler = new Handler(Looper.getMainLooper());// 创建子线程并启动它backgroundThread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("childThread prepare");Looper.prepare();// 在子线程中创建一个 Handler// 充当其他线程调用 backgroundHandler.sendMessage/backgroundHandler.post的接收端backgroundHandler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {System.out.println("childThread receive Msg 1");}}};System.out.println("childThread Looping");Looper.loop();}});backgroundThread.start();// 等待线程跑起来try {sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}Message message = backgroundHandler.obtainMessage(1);backgroundHandler.sendMessage(message);System.out.println("MainThread: sendMsg 1");}}
  • 日志 

2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread prepare
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread Looping
2024-11-07 16:25:56.910 12880-12880 System.out       com...myapplication  I  MainThread: sendMsg 1
2024-11-07 16:25:56.912 12880-12902 System.out       com...myapplication  I  childThread receive Msg 1

3. Handler使用的源码解析 

  • 3.0 调用端使用sendMessage来发送消息
Message message = backgroundHandler.obtainMessage(1);
backgroundHandler.sendMessage(message);

追sendMessage的调用栈,最终会调用到Handler.java的enqueueMessage函数

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;   // 
。。。。。return queue.enqueueMessage(msg, uptimeMillis);}

1)msg.target指定了this,即当前的Handler, 对应3.3中取出消息的地方msg.target为handler, 且这个Handler是backgroundHandler。

2)enqueueMessage将msg放入了消息队列。

  • 3.1 Looper.prepare();
    public static void prepare() {prepare(true);}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));}
  • 3.2 Handler backgroundHandler = new Handler(Looper.myLooper())
    public Handler(@NonNull Looper looper) {this(looper, null, false);}@UnsupportedAppUsagepublic Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {this(looper, callback, async, /* shared= */ false);}public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,boolean shared) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;mIsShared = shared;}
  • 3.3 Looper.loop(); 关键是loopOnce()从消息队列中循环拿消息,

假设现在调用了3.0中的sendMessage方法,此时消息队列中已经有了消息。

那么调用me.mQueue.next();即可拿到这条message.

再调用dispatchMessage处理消息。

private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // might block
。。。。msg.target.dispatchMessage(msg);。。。。。msg.recycleUnchecked();
。。。。。return true;}

3.0 讲过了msg.target是backgroundHandler,再往下查看dispatchMessage的实现:

    public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);  // 调用者overide 实现的 handleMessage函数}}

会发现其中的handleMessage是在 2.当中实现的handleMessage方法。

4. nativePollOnce(ptr, nextPollTimeoutMillis);

nativePollOnce, 就是linux中的poll函数,可以让出线程对cpu的占用。

没有消息则线程进入空闲状态,不用一直调用来浪费cpu, 这种等待是非阻塞的。

5.总结:

在子线程中:

  • Looper.prepare()初始化了MessageQueue,与当前线程绑定。
  • Handler通过传入当前的Looper实现与Loop绑定,
  • Handler通过override实现了handleMessage。
  •  再通过Looper.loop()开启死循环来处理消息。

其他线程:

  • 调用子线程的Handler的post/sendMessage方法,来向目标线程的MessageQueue写入消息。

子线程:

  • 子线程调用Loop.loop()拿到消息,并调用dispatchMessage处理消息。
  • 在调用子线程override的handleMessage方法来处理来自其他线程的消息。

http://www.ppmy.cn/ops/132041.html

相关文章

蓝桥杯介绍

蓝桥杯是全国性的IT类学科竞赛,全称为蓝桥杯全国软件和信息技术专业人才大赛。 一、竞赛特点 涵盖多个领域 蓝桥杯竞赛涵盖了多个领域,包括软件开发、电子设计、嵌入式设计等。不同领域的竞赛内容和要求各不相同,但都注重考查学生的实践能力和创新能力。例如,软件开发类竞…

Android 解决MTK相机前摄镜像问题

很莫名其妙的,前摄默认镜像,原来是为了前摄拍字体正确显示,比如自拍,前摄拍摄的人像虽左右镜像了,但如果后面有字牌显示,字体会显示正常而不是翻转。但现在需求是满足普遍的前摄原生代码不带镜像修改&#…

对称二叉树(力扣101)

题目如下: 思路 对于这道题, 我会采用递归的解法. 看着对称的二叉树, 写下判断对称的条件, 再进入递归即可. 值得注意的是, 代码中会有两个函数, 第一个是isSymmetric,第二个是judge. 因为这里会考虑到一种特殊情况, 那就是 二叉树的根结点(最上面的那个),它会单独用…

内容创作太难?分享五个偷懒工具!

在数字内容创作日益繁荣的今天,选择高效、优质的创作资源平台是每位创作者提升作品质量的关键。以下是几个内容创作必备的网站推荐,涵盖了设计、文案、灵感、图片等多个领域,助您在创作的道路上事半功倍。 一、UI中国UI中国官网https://www.u…

LeetCode100之旋转图像(48)--Java

1.问题描述 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例1 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&…

第七篇: BigQuery中的复杂SQL查询

BigQuery中的复杂SQL查询 背景与目标 在数据分析中,我们通常需要从多个数据源中获取信息,以便进行深入的分析。这时,BigQuery提供的JOIN、UNION和子查询等复杂SQL语句非常实用。本文将以Google BigQuery的公共数据集为例,介绍如何…

提示工程(Prompt Engineering):大模型微调Prompt/Instruct Mode;稀疏向量与稠密向量进行词语编码

目录 提示工程(Prompt Engineering):大模型微调Prompt/Instruct Mode 稀疏向量与稠密向量 稀疏向量 稠密向量 稀疏向量与稠密向量的区别 提示工程(Prompt Engineering):大模型微调Prompt/Instruct Mode 如今已不再是一个陌生的概念,它作为一种“有效地与人工智能沟…

Python 继承、多态、封装、抽象

面向对象编程(OOP)是 Python 中的一种重要编程范式,它通过类和对象来组织代码。OOP 的四个核心概念是继承(Inheritance)、多态(Polymorphism)、封装(Encapsulation)和数据…