Android HandlerThread详解
概述
详细
原文地址:
一、准备工作
开发环境:
jdk1.8
Eclipse Luna Service Release 1 (4.4.1)
运行环境:
华为荣耀6(Android4.4)、华为p9(Android7.0)
实现功能:
Android HandlerThread的使用
二、程序实现
1、需要截图程序结构
HandlerThread类介绍
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.
HandlerThread是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例。注意:start()仍然必须被调用。
如下是HandlerThread使用的demo。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | package com.zpengyong.hand; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private final static String TAG = "MainActivity" ; private Button mGet; private TextView mResult; protected final int MSG_GET = 1 ; protected final int MSG_RESULT = 2 ; private HandlerThread mHandlerThread; //子线程中的Handler实例。 private Handler mSubThreadHandler; //与Ui线程绑定的Handler实例。 private Handler mUiHandler = new Handler(){ public void handleMessage(Message msg) { Log.i(TAG, "mUiHandler handleMessage thread:" +Thread.currentThread()); switch (msg.what) { case MSG_RESULT: mResult.setText((String)msg.obj); break ; default : break ; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "onCreate thread:" +Thread.currentThread()); mGet = (Button) findViewById(R.id.get); mGet.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { mSubThreadHandler.sendEmptyMessage(MSG_GET); } }); mResult = (TextView) findViewById(R.id.result); initHandlerThraed(); } private void initHandlerThraed() { //创建HandlerThread实例 mHandlerThread = new HandlerThread( "handler_thread" ); //开始运行线程 mHandlerThread.start(); //获取HandlerThread线程中的Looper实例 Looper loop = mHandlerThread.getLooper(); //创建Handler与该线程绑定。 mSubThreadHandler = new Handler(loop){ public void handleMessage(Message msg) { Log.i(TAG, "mSubThreadHandler handleMessage thread:" +Thread.currentThread()); switch (msg.what){ case MSG_GET: try { //模拟延时处理 Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } double number = Math.random(); String result = "number:" +number; //向ui线程发送消息,更新ui。 Message message = new Message(); message.what = MSG_RESULT; message.obj = result; mUiHandler.sendMessage(message); break ; default : break ; } }; }; } @Override protected void onDestroy() { super .onDestroy(); Log.i(TAG, "onDestroy" ); //退出HandlerThread的Looper循环。 mHandlerThread.quit(); } } |
上述代码比较简单,功能也比较简单,可以在此基础上进行扩展。
在Actvitiy创建的时候调用initHandlerThraed()函数:
-
创建HandlerThread线程
-
运行线程
-
获取HandlerThread线程中的Looper实例
-
通过Looper实例创建Handler实例,从而使mSubThreadHandler与该线程连接到一起。
多次点击按钮,打印信息如下所示:
1 2 3 4 5 6 | 07-13 05:15:07.662: I/MainActivity(1472): onCreate thread:Thread[main,5,main] 07-13 05:15:45.382: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main] 07-13 05:15:46.402: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main] 07-13 05:15:46.412: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main] 07-13 05:15:47.412: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main] ..... |
点击按钮,向mSubThreadHandler发送消息,mSubThreadHandler中接收到消息进行处理,由打印可知mSubThreadHandler的handleMessage方法运行在子线程中。
模拟耗时操作,生成随机数,然后向主线程中(mUiHandler)发送消息(Message)。
mUiHandler的handleMessage方法运行在主线程,可以用来更新Ui界面。
Activity销毁的时候,调用mHandlerThread.quit(),退出HandlerThread的Looper循环。
效果图如下:
【运行方式:右键项目:Run as -》Android Application (备注:Eclipse需要配置Android开发环境)】
三、源码解析
源码路径路径:frameworks/base/core/Java/android/os/HandlerThread.java
先看下HandlerThread的构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class HandlerThread extends Thread { int mPriority; int mTid = - 1 ; Looper mLooper; //@param name 线程名 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是Thread(线程)的子类。创建一个HandlerThread实例,也就是创建了一个特殊的线程实例。
HandlerThread提供了两个构造方法:
-
HandlerThread(String name) 参数为线程名称,线程优先级为Process.THREAD_PRIORITY_DEFAULT。
-
HandlerThread(String name, int priority),name为线程名称,priority为设置的线程优先级。
我们知道线程需要通过start()方法来运行线程,HandlerThread也是这样的。接着看下线程运行的run()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Overridepublic void run() { //获取进程id mTid = Process.myTid(); //创建Looper实例 Looper.prepare(); synchronized ( this ) { //获取当前线程的Looper实例 mLooper = Looper.myLooper(); notifyAll(); } //设置线程优先级 Process.setThreadPriority(mPriority); onLooperPrepared(); //开始循环 Looper.loop(); mTid = - 1 ; } |
由run方法可知HandlerThrea线程运行创建了Looper实例,并开启了Looper循环,循环从消息队列中获取消息并给Handler进行处理。对于Looper不太明白的可以参考这篇深入理解Handler、Looper、Messagequeue
onLooperPrepared()在Looper循环之前调用,如果需要在Looper循环之前执行一些设置,可以显式覆盖此方法。
接着看获取Looper实例
1 2 | //获取HandlerThread线程中的Looper实例 Looper loop = mHandlerThread.getLooper(); |
对应源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //此方法返回与此线程关联的Looper。 如果此线程未启动或由于任何原因isAlive()返回false,此方法将返回null。 public Looper getLooper() { if (!isAlive()) { return null ; } // 如果这个线程已经启动,将会被阻塞,直到mLooper被初始化为止。 synchronized ( this ) { while (isAlive() && mLooper == null ) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } |
mHandlerThread.getLooper()获取与该线程绑定的Looper实例。mLooper是在HandlerThread的run()方法中赋值的(也就是在子线程中),getLooper是我们在主线程中调用,该方法会阻塞直到mLooper赋值。
然后demo中通过该looper实例创建Handler.
1 2 | //创建Handler与该线程绑定。 mSubThreadHandler = new Handler(loop) |
你可能会好奇为什么要这样长久Handler而不是“new Handler()“这样呢?因为我们要创建的Handler要与子线程绑定到一起,要处理子线程中的消息,所以要通过子线程中的looper(有线程对应的消息队列)实例创建Handler。这样通过mSubThreadHandler发送的消息会添加到子线程中的消息队列中,然后Looper实例消息进行分发,交给mSubThreadHandler进行处理。
HandlerThread提供的线程退出方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public boolean quit() { Looper looper = getLooper(); if (looper != null ) { looper.quit(); return true ; } return false ; } public boolean quitSafely() { Looper looper = getLooper(); if (looper != null ) { looper.quitSafely(); return true ; } return false ; } |
quit和quitSafely都是退出HandlerThread的消息循环。其分别调用Looper的quit和quitSafely方法。
quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息
HandlerThread适合处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。而网络操作相对比较耗时,容易阻塞后面的请求,因此在这个HandlerThread中不适合加入网络操作。
至此HandlerThread就说完了。有什么问题欢迎大家指正、交流。
四、其他补充
参考文章:
深入理解Handler、Looper、Messagequeue
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?