Android线程和线程池(1)——线程的介绍和HandlerThread
文章目录
- 对Android线程的基础知识进行阐述
- 阐述Android事件驱动框架中的genicore模块的功能与实现
- 介绍Handled Thread在 Android开发中的应用及其工作原理
- 详细说明Handled Thread的具体实现过程
- 分析Handled Thread的特性与优势
- 对Handled Thread源代码进行解析与分析
1.Android线程的基本介绍
Android中线程的概念具有重要性。从其功能角度来看,线程主要可分为两大类:主线程和子线程。其中主线程主要负责处理与用户界面交互相关的任务,而子线程则通常用于执行那些需要较长时间完成的任务。
除了Thread自身之外,在Android环境中还有许多其他类能够担当作为传统线程的角色。例如AsyncTask和IntentService等情况。此外HandlerThread也是一种独特且具有特定功能的专有类。然而这些类在呈现形式上与传统多态类有所不同。它们的本质依然是基于Java虚拟机的核心机制。不同类型的Java虚拟机内核组件在本质上都继承自基本多态类框架,并根据不同的需求设计了各自独特的功能模块和应用场景。
2.AsyncTask
长久以来,在Android开发领域中AsyncTask发挥着至关重要的作用。该类主要承担着执行非长时异步任务的职责,并可作为替代Thread + Handler框架的功能补充工具,在异步操作的同时动态展示界面效果。然而由于context泄露、回调遗漏、configuration变更可能导致崩溃风险等问题以及平台间的差异性因素,在API 30(Android 11)版本中AsyncTask正式被弃用:
官方文档说明:
AysncTask旨在简化对UI线程的集成使用。然而,在大多数情况下被用于整合到UI组件中。
这种情况会导致Context泄露、回调未被捕获或配置更改时崩溃。
此外,在不同版本的平台上表现出不一致的行为模式。
它会捕获来自doInBackground的任务异常,并没有提供比直接使用Executors更多的便利性。
这种设计选择并未带来实质性的功能提升。
被弃用后,Android给出了两个替代的建议:
- 在java.util.concurrent包中存在一系列关联类(包括Executor、ThreadPoolExecutor和FutureTask)。
- 在Kotlin中并行处理的一个重要工具是异步编程框架(coroutine),它支持协同工作机制。
具体的可以参考如下博客:
Android多线程:AsyncTask的工作原理详细阐述(上)
android多线程-AsyncTask之工作原理深入解析(下)
3.HandlerThread
作为Thread的子类之一,在设计上类似于将Handler与线程机制相结合。该类不仅继承了基本功能,并且内置了一个Looper组件(即循环机制),从而能够通过消息队列系统实现对当前线程的高效重复利用。然而,在实际应用中也存在一定的局限性:每个任务都被组织成一个独立的队列形式运行,在这种情况下若某个任务的执行耗时过长,则会导致后续所有依赖该任务的任务不得不等待其完成才能继续运行。
在实现机制上,他与普通线程的主要区别在于内部维护了一个looper。正是由于这一点,使得他能够创建相应的Handler来完成相关操作。
由于Looper内部存在一个无限循环机制,在我们停止使用HandlerThread时,则必须调用该对象的quit方法来终止其运行。
优势:
- 通过将loop分配到子线程进行处理,不仅减少了主线程序的负担压力,并且使整体流程更加高效。
- 单个程序能够模拟多个端口的功能,在一定程度上实现了多端口的操作。
- 独立的消息队列系统能够保证事务提交而不影响用户界面操作。
劣势:
- 每当任务队列逐步执行完毕后,若耗时过长,则会导致消息延迟。
- 针对IO类操作而言,在线程运行过程中会出现等待现象,并非所有操作均可同时进行。
3.1工作原理
内部原理 = Thread类 + Handler类机制,即:
- 利用Thread类作为基础,迅速地生成一个带Loop对象的新工作线程.
- 将Handler类打包成一个实例(如Handler &),方便地生成该实例并与其它线程实现通信.
3.2使用步骤
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
代码解释
完整实例:
public class MainActivity extends AppCompatActivity {
Handler mainHandler,workHandler;
HandlerThread mHandlerThread;
private final String[] url ={
"",
"",
"",
"",
"",
"",
""
};
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
initView();
initData();
}
private void initView() {
imageView= (ImageView) findViewById(R.id.image);
mainHandler = new Handler(getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
ImageModel model = (ImageModel) msg.obj;
if(model.getBitmap()!=null){
imageView.setImageBitmap(model.getBitmap());
}
}
};
}
private void initData() {
HT_DownloadThePictures();
}
private void HT_DownloadThePictures() {
/* * 步骤1:创建Handler实例对象
* 传入参数 = 线程名称,作用 = 标记改线程
*/
mHandlerThread = new HandlerThread("handlerThread");
/* * 步骤2:启动线程
*/
mHandlerThread.start();
/* * 步骤3:
* 创建工作线程Handler & 复写handleMessage()
* 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
* 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
*/
workHandler = new Handler(mHandlerThread.getLooper(),new childCallback());
/* * 步骤4:
* 使用工作线程Handler向工作线程的消息队列发送消息
* 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
*/
for(int i = 0; i < 7; i++){
/* * 使用工作线程Handler向工作线程的消息队列发送消息
* 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
*/
workHandler.sendEmptyMessageDelayed(i,1000*i);
}
}
class childCallback implements Handler.Callback{
@Override
public boolean handleMessage(@NonNull Message msg) {
//在子线程进行网络请求
Bitmap bitmap = downloadUrlBitmap(url[msg.what]);
ImageModel imageModel = new ImageModel();
imageModel.setBitmap(bitmap);
imageModel.setUrl(url[msg.what]);
Message msg1 = new Message();
msg1.what = msg.what;
msg1.obj = imageModel;
mainHandler.sendMessage(msg1);
return false;
}
}
private Bitmap downloadUrlBitmap(String urlString) {
/*
1. HttpURLConnection网络请求加载
*/
/*
Bitmap bitmap = null;
HttpURLConnection httpURLConnection = null;
InputStream in = null;
try {
URL url = new URL(urlString);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setReadTimeout(8000); //设置读取超时
httpURLConnection.setConnectTimeout(8000); //读取连接超时
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
in = httpURLConnection.getInputStream();
bitmap = BitmapFactory.decodeStream(in);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
*/
/*
2.Glide加载
*/
FutureTarget<Bitmap> futureTarget =
Glide.with(getApplicationContext())
.asBitmap()
.load(urlString).submit(100,100);
Bitmap bitmap = null;
try {
bitmap = futureTarget.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Do something with the Bitmap and then when you're done with it:
Glide.with(getApplicationContext()).clear(futureTarget);
return bitmap;
}
}
//ImageModel.java
public class ImageModel {
private Bitmap bitmap;
private String url;
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
代码解释
3.3 HandlerThread的特点
- 将循环转移到子线程处理后会减轻主线程负担。
- 启动一个核心 line thread相当于多 thread协同工作,在消息传递的先后顺序下完成任务串行处理。
- 每个 task都会依次入队等待执行,在某个 task耗时过长的情况下会导致后续 task被延迟。
- HandlerThread 独立拥有消息队列系统,并不干扰或阻塞 UI thread.
- 对于网络 I/O 操作来说, HandlerThread并不适合,因为它只有一个核心 thread, 并必须依次轮流接收每个 I/O 请求.
3.4 源码分析
先来看构造函数
public class HandlerThread extends Thread {
int mPriority; //线程优先级
int mTid = -1;
Looper mLooper; //当前线程持有的Looper对象
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
protected void onLooperPrepared() {
}
代码解释
通过查看源码可知HandlerThread继承自Thread类。其构造函数接收两个参数:一个是name字段用于标识线程名称;另一个是priority字段表示线程优先级。根据需求即可完成使用。其中成员变量mLooper即为HandlerThread自身维护的Looper对象。onLooperPrepared()此方法为空实现,供我们在必要时进行功能扩展。特别提醒重写操作应放置在Loop循环启动之前,并考察一下run方法的行为:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
代码解释
我们已有对HandlerThread常规使用的了解,在创建HandlerThread对象之后会自动启动其他操作流程(即会触发run方法);这实际上导致该线程被启动(即后续操作流程会被触发)。此时Looper对象将会被创建(当Looper准备完成时该对象会被绑定到当前异步线程上),这样我们就可以将其赋值给Handler对象以确保其handleMessage函数能够在异步线程中运行(接下来执行以下代码):
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
代码解释
在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,在最后执行一次Looper.loop();代码以开启looper循环语句。为什么需要唤醒等待线程?我们可以进一步了解这一点,请查看getLooper方法。
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;
}
代码解释
事实上可以看出外部在调用getLooper方法获取looper对象时首先会检查当前线程是否已启动。如果当前线程已处于运行状态则程序将执行同步机制并检查Looper变量是否为null若该变量确实为null则表明Looper对象尚未被赋值即此时current thread尚未完成创建looper object的过程而处于等待状态直至looper object被成功创建并通过notifyAll()方法唤醒wait threads随后返回完整的looper object引用。之所以采用这种设计是为了确保主线程与子线程之间的数据一致性因为looper object的生成是在子线程环境中完成的而get_looper方法位于主线程因此无法预先知道子线程中是否已经生成了有效的looper instance这就导致了必须借助wait and notify机制来保证各线程之间的协调运行从而最终实现主线程能够可靠地获取到一个经过验证的完整looper instance。在这种情况下HandlerThread内部实现了通过wait and notify机制来解决潜在的synchronization问题从而保证了整个系统的稳定性和可靠性。
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 方法时,默认会触发 Looper 的 quit 方法,并最终导致 MessageQueue 中的 removeAllMessagesLocked 方法被激活(基于 Handler 消息机制的知识点),该函数的主要作用就是将 MessageQueue 消息池中的所有消息彻底清除,其中包括延迟消息(这些延迟消息是由 sendMessageDelayed 或 postDelayed 等方法创建)以及非延迟消息。而当使用 quitSafely 方法时,默认会同样是通过 Looper 的 quitSafely 方法来实现,并最终导致 MessageQueue 中的 removeAllFutureMessagesLocked 方法运行(该函数的作用则是仅清除掉所有延迟消息,并将所有非延迟消息发送至消息池以便 Handler 处理完毕后再停止整个 Looper 循环)。因此相比普通的 quit 方法而言,quitSafely 的安全特性在于它在清除消息之前会先释放所有的非延迟消息到 Handler 处理以确保后续操作的安全性
该博客详细阐述了Android开发中HandlerThread这一核心知识点,并深入探讨其运作机制如何实现多线程通信。文章通过大量代码示例演示了HandlerThread的实际应用场景,并结合实际项目分析了其性能优化技巧与常见问题解决方案。
