HandlerThread源码解析

一、HandlerThread简介

在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的?那有什么解决方法呢?

  • 使用线程池
  • 使用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.

一个好用的类用于创建一个自带Looper的线程。这个Looper可以用来创建Handler。注意start()方法必须首先被调用。

1.HandlerThread使用场景

HandlerThreadGoogle帮我们封装好的,可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用HandlerLooper实现的。

2.HandlerThread简单使用
//创建mHandlerThread
mHandlerThread = new HandlerThread("main");
//获取HandlerThead中的Looper
Looper looper = mHandlerThread.getLooper();
//创建子线程中的Looper
Handler handler = new Handler(looper);
//执行耗时操作
handler.post(new Runnable() {@Overridepublic void run() {//子线程中执行耗时操作}
});

//界面销毁的时候需要销毁Looper
@Override
protected void onDestroy() {super.onDestroy();mHandlerThread.quit();
} 

二、HandlerThread源码分析

1.成员变量
int mPriority; // 线程优先级
int mTid = -1; // 线程ID
Looper mLooper; // 创建线程的Looper 
2.构造方法
public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {super(name);mPriority = priority; // 线程的优先级
} 

有两个构造方法,一个方法和两个方法,name是线程的名称,priority是线程的优先级。

3.启动线程要调用start()方法,实际上就是执行了run()方法
public void run() {mTid = Process.myTid();Looper.prepare();//持有锁机制来获得当前线程的Looper对象synchronized (this) {mLooper = Looper.myLooper();//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的waitnotifyAll();}//设置线程的优先级别Process.setThreadPriority(mPriority);//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展onLooperPrepared();Looper.loop();mTid = -1;
} 
4.那么为什么会有锁机制以及notifyAll()方法,原因就在getLooper()方法
public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.// 如果线程已经启动,请等到创建了Looper。synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;
} 

在获取mLooper对象的时候存在一个同步问题,只有等到线程启动以及Looper对象成功创建之后才能获得mLooper的值。

5.下面看下quit()quitSafe()两个方法
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;
} 

这两个方法的位移区别就是looper.quit()looper.quitSafely(),分别跟踪这两个方法

public void quit() {mQueue.quit(false);
}
public void quitSafely() {mQueue.quit(true);
} 

mQueue.quit()方法一个传入了false一个传入了true

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);}
} 

不安全的会调用removeAllMessagesLocked()这个方法,我们来看这个方法是怎样处理的,其实就是遍历Message链表,移除所有信息的回调,并重置为null

private void removeAllMessagesLocked() {Message p = mMessages;while (p != null) {Message n = p.next;p.recycleUnchecked();p = n;}mMessages = null;
} 

安全地会调用removeAllFutureMessagesLocked()这个方法,它会根据Message.when这个属性,判断我们当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。因此说quitSafe()是安全的,而quit()方法是不安全的,因为quit()方法不管是否正在处理消息,直接移除所有回调。

private void removeAllFutureMessagesLocked() {final long now = SystemClock.uptimeMillis();Message p = mMessages;if (p != null) {//判断当前队列中的消息是否正在处理这个消息,没有的话,直接移除所有回调if (p.when > now) {removeAllMessagesLocked();} else {//正在处理的话,等待该消息处理处理完毕再退出该循环Message n;for (;;) {n = p.next;if (n == null) {return;}if (n.when > now) {break;}p = n;}p.next = null;do {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}
} 

三、总结

  • 如果经常要开启线程,接着又是销毁线程,这是很耗性能的,HandlerThread很好的解决了这个问题;
  • HandlerThread由于异步操作是放在Handler的消息队列中的,所以是串行的,但只适合并发量较少的耗时操作。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值