Android多线程之 HandlerThread 源码分析

Android多线程之HandlerThread详解 一 HandlerThread介绍 当系统中有多个耗时任务需要执行时,每个任务都会开启一个新线程去执行耗时任务,这样会导致系统多次创建和销毁线程,从而影响性能。为了解决这一问题,Android 提供了 HandlerThread,HandlerThread 是在线程中创建一个 Looper 循环器,让 Looper 轮询消息队列,当有耗时任务进入队列时,则不需要开启新线程,在原有的线程中执行耗时任务即可,否则线程阻塞。 二 工作原理 HandlerThread 继承 Thread,在 Thread 开始执 阅读详情

Android多线程之 HandlerThread 源码分析

一、前言

这主要是一个讲解 Android 中多线程的系列,全部文章如下:

前面写了 Android 中的消息机制,也就是 Handler 的消息机制。不了解的可以去看下 Handler系列—源码分析。

本章就来使用和分析下 HandlerThread。

二、什么是 HandlerThread?

我们看下官方的描述:

可以看到 HandlerThread 是继承于 Thread 类的,说明我们可以使用它开启一个子进程去执行一些操作。

再看下官方说明:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

这个类可以方便的开启一个带有 Looper 的新线程。这个 looper 可以去创建一个 Handler,但是还是必须得调用 start() 方法。

我们大概知道 HandlerThread 更加方便了我们在 Android 中进行多线程消息传递。

下面我们通过实例了解下:

public class HandlerThreadTestActivity extends AppCompatActivity {

    private static final String TAG = "HandlerThreadTest";

    private TextView mShow;

    private HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread_test);
        mShow = findViewById(R.id.show);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message message) {
                super.handleMessage(message);
                final int what = message.what;
                mShow.setText("得到的 what 为: "+what );
                Log.d(TAG, "handleMessage: " + message.what);
            }
        };
        findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(1));
            }
        });

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(2));
            }
        });

        findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(3));
            }
        });
    }
}

运行程序,不出意外会得到如下异常:

原因已经很清楚了,我们在子线程中做了更新 UI 的操作。

下面来分析下原因:

由于 HandlerThread 是继承于 Thread 的,所以在实例化的 HandlerThread 对象是一个子线程,我们在创建 Handler 的时候,传入了 mHandlerThread 对象所在的线程的 Looper 对象,一个线程中只能有一个 Looper 对象,进而使 mHandler 和 子线程进行了绑定,所以这个 mHandler 是属于 mHandlerThread 的。

大家都知道只有主线程的 Handler 对象才能够进行更新 UI 的操作,所以在 mHandler 中执行 UI 操作时肯定不行的。

所以我们需要这样写:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        mShow.setText("得到的 what 为: "+what );
    }
});

代码 才能真正的运行。

通过上面的分析,大家应该能够知道 HandlerThread 是什么了。

  • 由于继承于 Thread ,所以 HandlerThread 就是一个线程类;
  • HandlerThread 内部拥有一个 Looper 循环,可以直接传递给 Handler 使用

其实 HandlerThread 就是一个包含了 Looper 的 Thread,可以免去我们在子线程中创建 Handler 的时候写一些多余的代码。

比如我们在上篇文章的 2-1 章节 章节中创建一个子线程中的 Handler :

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        Handler threadHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        Looper.loop();
    }
}).start();

使用了 HandlerThread 以后我们只需要:

private HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
Handler threadHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

这样看上去是不是清晰了许多?就是省去了创建 Looper 和 开启 loop 的操作

三、HandlerThread 解析

由于这个类的代码很少,我们都看下。

1.成员变量和构造方法

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
}

可以看到,HandlerThread 的成员变量很简单,

  • mPriority 是优先级
  • mTid 是线程的标示符,主要和线程的优先级有关
  • mLooper 是内部持有的 Looper 对象
  • mHandler 是与之绑定的 Handler 对象

构造方法也只有两个:

  1. 第一个构造函数
    第一个只需要传入线程的名称就行了,会把线程的优先级设置了默认的

  2. 第一个构造函数
    第二个则根据自己的需要传入线程的名称和优先级,根据注释可以知道,这个优先级必须是从 android.os.Process 类中获取,不能从 Thread 类中获取

2、onLooperPrepared 方法

继续往下看,会看到一个空实现的方法

/**
 * Call back method that can be explicitly overridden if needed to execute some
 * setup before Looper loops.
 */
protected void onLooperPrepared() {
}

根据注释可以知道,这个方法是给我们在创建 Looper 之后,开启 Looper 循环之前的一些操作使用的。

也就是说在 Looper.prepare() 之后 ,在 Looper.loop() 之前需要做的操作,写在这里,上一篇 Handler 源码解析的文章有写到,这里就不多说了。

3、核心 run 方法

由于继承于 Thread,要重写 Thread 的核心方法 run() 方法。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

这里首先会调用 Looper.prepare() 创建一个新的 Looper 对象,关于里面的内容,如果看过上篇文章,应该就知道了。

创建完 Looper 以后,通过 Looper 的 Looper.myLooper() 赋值给其成员变量 mLooper。

Process.setThreadPriority(mPriority); 就是设置线程的优先级,
onLooperPrepared(); 就是上面的空方法
Looper.loop(); 开启 Looper 循环,不断的查看 MessageQueue 中有没有新消息
最终执行完以后把 mTid置为 -1。

可能你发现了 notifyAll(); 这个方法,那么是干嘛的呢?其实我在 Java多线程 有讲到:

Object 类中提供了 wait() 方法,导致当前线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法,才会继续执行。

其实这个地方的 notifyAll() 和下面的 getLooper() 代码是有关的。

4. 获取 Looper

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

代码中先判断了是线程是否存活,如果不存活,就直接返回 null.

然后我们就看到了的上面提到的 wait() 方法,也就是说如果这个线程没有执行 run() 方法之前,就去调用 getLooper() 是拿不到的。只有执行了 run() 方法了以后,执行了 notifyAll 才会继续解除这里的 wait 等待,才能正确的获取到 mLooper 对象。

5. 其他方法

@NonNull
public Handler getThreadHandler() {
    if (mHandler == null) {
        mHandler = new Handler(getLooper());
    }
    return mHandler;
}

getThreadHandler 获取一个和当前线程关联的 Handler 对象

public boolean quit() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quit();
        return true;
    }
    return false;
}

quit 退出 Handler 线程的循环

public boolean quitSafely() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quitSafely();
        return true;
    }
    return false;
}

quitSafely 安全的退出 Handler 线程的循环

上面的 quit 和 quitSafely 都调用了 MessageQueue 中的方法:

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
}
  1. quit 调用了 removeAllMessagesLocked();

    作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

  2. quitSafely 调用了 removeAllFutureMessagesLocked()

    该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

无论调用了 quit 还是 quitSafely,线程中的 Looper 都不会接受新的消息了,消息队列终结,这时候再通过 Handler 调用 sendMessage 或 post 等方法发送消息时均返回 false ,表示消息没有成功放入消息队列 MessageQueue 中,因为消息队列已经退出了。

四、小结

至此 HandlerThread 的源码已经分析完了,相对来说 HandlerThread 就是做了一些封装,理解和用起来都是不难的。

下篇文章看下 AsyncTask。

【硬核指南/MI50 32G/Ubuntu】从零部署大模型:MI50显卡Ubuntu系统驱动安装与避坑全记录 本文详细记录了在Ubuntu系统上为AMD MI50显卡安装驱动并部署大模型的完整流程,包括硬件兼容性检查、BIOS关键设置、系统安装优化、ROCm驱动安装及常见问题解决方案。特别针对MI50显卡在X99平台上的特殊配置需求提供了实用技巧,帮助用户避免常见陷阱,提升大模型训练效率。 阅读详情

相关推荐

【AIGC】人工智能训练师(高级)考试题库-橙点同学

例如,在嘈杂的环境中进行声音转文字,即使是优秀的模型也可能会出现一些错误,但这并不意味着该模型本身质量差,在其他较为理想的环境下,它可能会有很好的表现。例如,在一些语音应用和系统中,会使用SSML来实现对语音合成的精细控制,如调整语速、语调、音量等,而这些应用和系统的开发往往会遵循W3C的相关标准和框架,其中就包括SSML这一语音合成标记语言。例如,在语音识别等场景中,像“去火车站怎么走”这样的语音指令,没有其他嘈杂的背景声音或模糊不清的表述,就属于无噪音的数据,有利于系统准确地进行处理和分析。

RisunJan的博客 9903

HandlerThread 简介

可以使用HandlerThread来处理本地的IO读写的工作,因为本地IO操作大多数耗时都是毫秒级的,所以对于HandlerThread这种单线程 + 队列的形式,不会产生阻塞。优先级越高的线程,获的的CPU资源越多,反之越少。所以如果其中某一个线程 任务执行时间过长,就会导致后续的任务都会被延迟处理。由于HandlerThread是单线程同步队列的,所以不适合处理网络IO的操作。是没有必要设置这么高的优先级的,因而需要我们根据需求,来设置合理的优先级别。线程的优先级,一定程度的优化线程。

m0_49508485的博客 826

macOS 12.7.6部署Ollama+Dify避坑指南

本文介绍了在macOS 12.7.6系统下部署Ollama和Dify的详细步骤及避坑指南。主要内容包括:1)关闭系统完整性保护(SIP)以允许修改系统目录;2)安装特定版本的Docker(2.1.0.5)和docker-compose(v2.38.2)以避免兼容性问题;3)配置Ollama服务并下载nomic-embed-text和deepseek-r1:7b模型;4)针对macOS调整Dify的docker配置文件,包括延长超时时间、修改存储路径、增加资源限制等关键设置。文中提供了完整的命令操作步骤和配置

Hi20240217的博客 3084

HandlerThread的使用方法和原理

我们如果想要在子线程中使用Handler,必须要手动创建一个Looper(原理已经在前面关于Handler和Looper的博客中介绍过:http://blog.csdn.net/whsdu929/article/details/52487605):new Thread(){ @Override public void run() { Looper.prepare();

AndroidConqueror的博客 716

USB之ch376s数据手册

         最近师兄交给我一个项目,虽说刚学嵌入式起步,但上手一个项目怎说都是最好的快速学习方式。因为刚了解ch376s管理U盘的数据,所以先是看了ch376s的数据手册。那我就把数据手册直接放上来吧。。。。      1、概述 CH376 是文件管理控制芯片,用于单片机系统读写 U 盘或者 SD 卡中的文件。 CH376 支持 USB 设备方式和 USB 主机方式,并且内置了 USB 通...

iBGG的博客 5995

中文实体关系抽取数据集大盘点:从百度Duie2.0到医疗CBLUE,哪个更适合你的NLP项目?

本文全面分析了中文实体关系抽取领域的主流数据集,包括百度DuIE 2.0、医疗CBLUE等,帮助开发者根据项目需求选择最适合的数据集。文章详细对比了各数据集的特点、适用场景及优化策略,为自然语言处理项目提供实用指南。

weixin_29284657的博客 115

自定义工作线程 HandlerThread + new Handler(handlerThread.getLooper())

是一个继承自 Thread 的类,它在其内部初始化了一个 Looper,因此它是一个可以运行消息循环的线程。这意味着你在 HandlerThread 中创建的 Handler 可以处理其内部消息队列中的消息,而这些消息的处理将在 HandlerThread 所代表的工作线程中完成,而不是在主线程。HandlerThread + Handler 结合使用通常是在需要执行耗时任务(例如文件读写、大量计算、长时间等待的网络请求等),为了避免阻塞UI线程,会创建一个工作线程(如使用。

小牧在一直在学习,在前进的道路上大家一起学习,进步。 935

BLE蓝牙胎压监测

胎压监测系统新技术

qq_41851841的博客 1247

Android HandlerThread用法

参考文章:http://www.cnblogs.com/hnrainll/p/3597246.html 一、HandlerThread简介 首先我们来看看为什么我们要使用HandlerThread? 在我们的应用程序当中为了实现同时完成多个任务,所以我们会在应用程序当中创建多个线程。为了让多个线程之间能够方便的通信,我们会使用Handler实现线程间的通信。 下面我们看看如何在线程当中

红娃子 5961

Blazor Session设置

微软官方封装了一些浏览器的操作,其中就有Session的操作的封装ProtectedSessionStorage 微软文档因为我们知道,依赖注入的生命周期是Singleton程序Scoped网页:刷新,url直接跳转都会重置。Transient路由C# Blazor 学习笔记(10):依赖注入但是没有一个和浏览器绑定的生命周期。即浏览器关闭才会退出。这个是后端无法获取的,因为后端根本不知道浏览器何时关闭。这个只能用浏览器自带的缓存。

qq_44695769的博客 1783

Hive中的四种排序方式(order by,sort by,distribute by,cluster by)使用与区别详解

在平时的Hive数仓开发工作中经常会用到排序,而Hive中支持的排序方式有四种,这里结合具体的案例详细介绍一下他们的使用与区别: order by sort by distribute by cluster by 一、order by(全局排序) 1、作用:全局排序,只有一个reducer。 order by 会对输入做全局排序,因此只有一个reducer(多个reducer无法保证全局有序),也正因为只有一个reducer,所以当输入的数据规模较大时会导致计算时间较长。   set hive.mapr

weixin_43230682的博客 1万+

Android多线程:HandlerThread学习指南(含具体使用、原理 & 源码分析)

前言 多线程的应用在Android开发中是非常常见的,常用方法主要有: 继承Thread类 实现Runnable接口 Handler HandlerThread AsyncTask 今天,我将献上一份全面 & 详细的HandlerThread机制的学习指南,内容包括:定性认知、定量使用、工作原理 & 源码分析,希望你们会喜欢。 目录 1. 定义 一个Andro...

Carson带你学Android 2935

Android 15.0 首次开机默认授予app运行时权限(去掉运行时授权弹窗)

在15.0的系统rom产品定制化开发中,在6.0以后对于权限的申请,都需要动态申请,所以会在系统首次启动后,在app的首次运行时, 会弹出授权窗口,会让用户手动授予app运行时权限,在由于系统产品开发需要要求默认授予app运行时权限,不需要用户默认授予运行时弹窗,所以需要在首次开机默认授予所有app运行时权限

安卓兼职framework和app工程师的博客 1198

Android多线程-HandlerThread用法与源码解析

使用HandlerThread好处在Android开发中,我们经常使用多线程编程。而多线程有多种方式实现。其中我们用得最多、也是最早接触的是使直接开一个线程Thread。new Thread(){ @Override public void run() { //do something here } }.start();先抛开这样开线程的弊端,因为在有时做一些

codetend的博客 675

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【Android+多线程】IntentService 知识总结:应用场景 / 使用步骤 / 源码分析

IntentService的定义、功能、应用场景、使用步骤、与Service和Thread的对比

qq_30885821的博客 718

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Android 多线程编程:IntentService & HandlerThread

因为 Android 是使用 Java 开发的,所以当我们谈及 Android 中的多线程,必然绕不过 Java 中的多线程编程。但在这篇文章中,我们不会过多地分析 Java 中的多线程编程的知识。我们会在以后分析 Java 并发编程的时候分析 Java 中的多线程、线程池和并发 API 的用法。 我们先来总结一下 Android 多线程编程的演变过程:首先是 Java 的 Thread。因为本身...

你好,开发者 923

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Android基础 -- Android HandlerThread

1.简介 AndroidThread是一个Android已经封装好的轻量级异步类,用来方便开发者更快的实现多线程、异步通信、消息传递等。 2.使用步骤 // 步骤1:创建HandlerThread实例对象 // 传入参数 = 线程名字,作用 = 标记该线程 HandlerThread mHandlerThread = new HandlerThread("handlerThre...

sinat_33150417的博客 424

多线程(四)、Android多线程使用及AsyncTask源码分析

本篇是多线程系列的第四篇,如果对前三篇感兴趣的也可以去看看。 多线程(一)、基础概念及notify()和wait()的使用 多线程(二)、内置锁 synchronized 多线程(三)、线程池 ThreadPoolExecutor 知识点总结 除了前面的线程池的使用外,在Android中,我们除了通过Thread创建线程外,还可以通过 AsyncTask、IntentServi...

Evan的博客 306

【Android】Handler工作原理 && HandlerThread用法

Handler在多线程通信,Handler通过Message、MessageQueue、Looper和Handler四大组件实现线程间通信,为解决线程重复启动问题,HandlerThread封装了Looper.prepare()和Looper.loop()逻辑,使单个线程可处理多个任务。HandlerThread如何在不同线程间传递任务结果,并分析了其内部synchronized同步机制保证线程安全的原理。最终实现后台线程执行耗时任务后,通过主线程Handler更新UI的效果。

阿华的博客 1297

Android 多线程之HandlerThread 完全详解

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52426353 出自【zejian的博客】 关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) android多线程-AsyncTask之工作原理深入解析(下)   之前对线程也写过几篇文章,不过倒是没有针对android,因为

Gabriel的专栏 176

IntentService 面试详解

前两篇文章介绍了AsyncTask与HandleThread,主要是针对Android的多线程编程。在这里补充下线程的知识。 线程是操作系统调度的最小单元,同时也是一种受限的资源(即不能无限制的创建和销毁,创建和销毁都会占用系统资源)。操作系统是通过时间片轮转的方式调度每个线程,因此不可能做到绝对的并行。 进程一般指一个执行单元,在PC或移动设备上指一个程序或者应用。一个进程可以包含一个或多个...

CoderZJ的博客 472

HandlerThread源码分析

HandlerThread源码分析 概述    在之前的文章中介绍过Android应用层的消息机制【点这里 】,也介绍了利用Handler机制实现多线程调度的AsyncTask,接下来介绍一个新成员HandlerThread。从字面上可以看出,HandlerThread可能是与Thread与Handler应用相关的,HandlerThread也确实是封装了Handle...

Leslie_Yu的博客 377

Android HandlerThread源码分析

HandlerThread继承于Thread类,是Android里的一个封装类,可以快速创建一个带有Looper对象的新线程。其本质是Thread+Handler。 HandlerThread可以用来执行多个耗时任务,不需要多次开启线程。

xiangxiongfly 252

Android多线程:HandlerThread详细使用手册

原文地址:https://blog.csdn.net/carson_ho/article/details/79285760 多线程的应用在Android开发中是非常常见的,常用方法主要有: 继承Thread类 实现Runnable接口 Handler AsyncTask HandlerThread 今天,我将全面解析多线程其中一种常见用法:HandlerThread 由于本文涉及...

wa172126691的博客 260

Android 多线程

多线程笔记 线程基础 线程基本使用api 线程池基础 ReentrantLock使用 阻塞分类 死锁定义实现以及解决方式 生产者消费者问题 AsyncTask源码分析

crazyZhangxl的博客 678

Android多线程:HandlerThread详细使用手册(含实例讲解)

前言 多线程的应用在Android开发中是非常常见的,常用方法主要有: 继承Thread类 实现Runnable接口 Handler AsyncTask HandlerThread 今天,我将全面解析多线程其中一种常见用法:HandlerThread 由于本文涉及多线程知识和Handler源码解析,所以阅读本文前建议先看: Android开发:Handler异步通信机制全面解...

Carson带你学Android 3万+
上一篇: 使用编译时注解实现简易的 ButterKnife 效果
下一篇: Android 多线程之 AsyncTask使用源码分析
Sean_css
博客等级 码龄11年 32粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值