前言
有些同学老问我,快要校招了 今年的金三银四退成了金四银五了,我改准备哪些知识点去面试复习,然而要准备的知识点很庞大,今天要讲的知识其中之一。一篇关于HandlerThread
的解析
知识汇总的PDF相关内容后续GitHub更新,想冲击金三银四的小伙伴可以找找看看,欢迎star
(顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)
使用场景
HandlerThread
是Google帮我们封装好的,可以用来执行多个耗时操作,而不需要 多次开启线程,里面是采用Handler
和Looper
实现的。
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
?
1. 创建HandlerThread
的实例对象
HandlerThread handlerThread = new HandlerThread("myHandlerThread" );
该参数表示线程的名字,可以随便选择。
1. 启动我们创建的HandlerThread
线程
handlerThread.start();
将我们的handlerThread
与Handler绑定在一起。 还记得是怎样将Handler与线 程对象绑定在一起的吗?其实很简单,就是将线程的looper
与Handler
绑定在一 起,代码如下:
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if(isUpdate){
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
注意必须按照以上三个步骤来,下面在讲解源码的时候会分析其原因
完整测试代码如下
public class MainActivity extends AppCompatActivity {
private static final int MSG_UPDATE_INFO = 0x100;
Handler mMainHandler = new Handler();
private TextView mTv;
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private boolean isUpdate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv);
initHandlerThread();
}
private void initHandlerThread() {
mHandlerThread = new HandlerThread("xujun");
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
mThreadHandler.sendEmptyMessage(MSG_UPDATE_I NFO);
}
}
};
}
/**
* 模拟从服务器解析数据
*/
private void checkForUpdate() {
try {
//模拟耗时
Thread.sleep(1200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前股票行情:%d";
result = String.format(result, (int) (Math.r andom() * 5000 + 1000));
mTv.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
isUpdate = true;
super.onResume();
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
isUpdate = false;
mThreadHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
mMainHandler.removeCallbacksAndMessages(null);
}
}
运行以上测试代码,将可以看到如下效果图(例子不太恰当,主要使用场景是在 handleMessage
中执行耗时操作)
HandlerThread
源码分析
官方源代码如下,是基于sdk23的,可以看到,只有一百多行代码而已。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if nee ded to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知g etLooper方法中的wait notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前 的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功 ,阻塞 synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
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;
}
/**
* Returns the identifier of this thread. See Process.myTid( ).
*/
public int getThreadId() {
return mTid;
}
}
1)首先我们先来看一下它的构造方法
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
有两个构造方法,一个参数的和两个参数的,name代表当前线程的名称,priority
为线程的优先级别
2)接着我们来看一下run()
方法,在run方法里面我们 可以看到我们会初始化一个Looper
,并设置线程的优 先级别
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLo oper方法中的wait notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备 ,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
- 还记得我们前面我们说到使用
HandlerThread
的时候必须调用 start() 方 法,接着才可以将我们的HandlerThread
和我们的handler绑定在一起吗?其实 原因就是我们是在run()
方法才开始初始化我们的looper
,而我们调用HandlerThread
的start()
方法的时候,线程会交给虚拟机调度,由虚拟机 自动调用run方法mHandlerThread.start(); mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { checkForUpdate(); if(isUpdate){ mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); } } };
- 这里我们为什么要使用锁机制和
notifyAll()
;,原因我们可以 从getLooper()
方法中知道public Looper getLooper() { if (!isAlive()) { return null; } // 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
总结: 在获得
mLooper
对象的时候存在一个同步的问题,只有当线程创建成功并且Looper
对象也创建成功之后才能获得mLooper
的值。这里等待方法wait和run方法 中的notifyAll
方法共同完成同步问题。
3)接着我们来看一下quit
方法和quitSafe
方法
//调用这个方法退出Looper消息循环,及退出线程
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//调用这个方法安全地退出线程
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
跟踪这两个方法容易知道只两个方法最终都会调用MessageQueue
的 quit(boolean safe)
方法
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 previou sly 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);
}
}
}
知识汇总的PDF相关内容后续GitHub更新,想冲击金三银四的小伙伴可以找找看看,欢迎star
(顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)