大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说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使用场景
HandlerThread
是Google
帮我们封装好的,可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler
和Looper
实现的。
2.HandlerThread简单使用
//创建mHandlerThread
mHandlerThread = new HandlerThread("main");
//获取HandlerThead中的Looper
Looper looper = mHandlerThread.getLooper();
//创建子线程中的Looper
Handler handler = new Handler(looper);
//执行耗时操作
handler.post(new Runnable() {
@Override
public 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方法中的wait
notifyAll();
}
//设置线程的优先级别
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
的消息队列中的,所以是串行的,但只适合并发量较少的耗时操作。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/37177.html