记住一点Handler是子线程到主线程,HandlerThread是主线程到子线程通信
一、HandlerThread简介
HandlerThread是一个轻量级的异步类,可以实现多线程,并且可以实现线程间的通信(HandlerThread主要应用是实现主线程到子线程的通信,子线程到主线程通信可以通过Handler机制)。
二、HandlerThread原理
既然已经有Handler可以实现线程间通信,为什么又设计了HandlerThread?
HandlerThread通过字面意思我们可以看到,它是Handler+Thread,那么我们猜测它应该实现了Handler和Thread功能,到底是不是呢,我们向下看。
首先,HandlerThread继承了Thread类,也就是说HandlerThread可以创建一个新的线程。
其次,HandlerThread内封装了Handler类,并自动创建了Looper和MessageQueue,也就是说我们使用HandlerThread线程时,不需要手动创建Looper。
优点:
之前我们使用Handler时,必须自己创建线程,并且自己创建Looper等相关的功能,而HandlerThread则提供了一种方便的方式,相当于内部已经集成了这些功能,不需要我们再手动创建Looper。
三、HandlerThread使用实例
使用步骤:
使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
//new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,
//进而绑定到了线程mHandlerThread
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) { 这个是在子线程
//对信息的相关处理操作
//在子线程mHandlerThread中运行
super.handleMessage(msg);
}
};
//4、创建消息并发送消息
//在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
mHandlerThread.quit();
通过使用步骤我们可以看到,HandlerThread实现的功能主要就是主线程向子线程通信,另外可以在使用Handler实现子线程到主线程的通信,进而就可以实现主线程到子线程间的双向通信.
使用实例:
使用实例:
//功能介绍:点击按钮实现延时操作,延时时间到后更新UI。
public class MainActivity extends AppCompatActivity {
TextView mTxtShowTest;
Button mBtnInnerClass ,mBtnHandlerThread, mBtnQuit;
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//匿名内部类,用于子线程向主线程通信用
private Handler mhandlerInnerClass = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
//执行的UI操作
mTxtShowTest.setText("点击了mBtnInnerClass");
break;
case 2:
//执行的UI操作
mTxtShowTest.setText("来自于mHandlerInHandlerThread的请求更新");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTxtShowTest = (TextView) findViewById(R.id.mTxtShowTest);
mBtnInnerClass = (Button) findViewById(R.id.mBtnInnerClass);
mBtnHandlerThread = (Button) findViewById(R.id.mBtnHandlerThread);
mBtnQuit = (Button) findViewById(R.id.mBtnQuit);
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
//new Handler(mHandlerThread.getLooper()),即把该Handler绑定
//到了mHandlerThread线程的Looper,进而绑定到了线程mHandlerThread
final Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//已经是在子线程mHandlerThread中运行了
//可以进行一些耗时等操作
try{
Thread.sleep(10000); //延时操作
}catch (Exception e){
e.getMessage();
}
//子线程向主线程通信
Message msg1 = Message.obtain();
msg1.what = 2;
mhandlerInnerClass.sendMessage(msg1);
super.handleMessage(msg);
}
};
mBtnInnerClass.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1;
mhandlerInnerClass.sendMessage(msg);
}
}).start();
}
});
mBtnHandlerThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//4、创建消息并发送消息
//主线程中
Message msg = Message.obtain();
//主线程向子线程发送信息,通信
mHandlerInHandlerThread.sendMessage(msg);
}
});
mBtnQuit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
mHandlerThread.quit();
}
});
}
}
注意:
当连续多次点击按钮mBtnHandlerThread是,mTxtShowTest中显示,并不是同时显示的,而是先显示第一次,间隔延时10s(这个10s是在复写handleMessage()方法时自己写的 Thread.sleep(10000))后显示第二次,再间隔延时的10s显示第三次。当我们连续多次点击mBtnHandlerThread按钮时,消息入队列,取出消息执行时,有一个延时10s,之后更新UI,这个消息才算执行完成,然后从消息队列中取出下一个消息,同样有一个延时10s,之后才是更新UI,所以即便时快速连续多次点击按钮,但是执行时每次都间隔一个延时10s.
总结:
以上就是HandlerThread的使用步骤和使用实例,已经测试过可以使用。首先点击按钮mBtnInnerClass, mTxtShowTest显示"点击了mBtnInnerClass",之后再点击按钮mBtnHandlerThread,十秒之后,mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新"。多次点击按钮mBtnHandlerThread,然后点击mBtnInnerClass,mTxtShowTest显示"点击了mBtnInnerClass",10s后mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新",之后再点击mBtnInnerClass,mTxtShowTest显示"点击了mBtnInnerClass",10s后又会mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新",即可证明前面注意中说明的问题。
四、HandlerThread源码分析
在第三部分使用实例中的使用步骤可以看到,使用HandlerThread大概需要5步,具体如下:
使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
//new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,
//进而绑定到了线程mHandlerThread
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//对信息的相关处理操作
//在子线程mHandlerThread中运行
super.handleMessage(msg);
}
};
//4、创建消息并发送消息
//在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
mHandlerThread.quit();
我们根据使用步骤 ,一步一步的去看源码。
步骤1、创建HandlerThread对象
源码分析:HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
/**
* 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 的定义,其继承Thread ,可知HandlerThread 就是个线程
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;
}
...
}
总结:
HandlerThread就是个线程,继承了Thread。创建HandlerThread 时,就相当于创建了一个新的线程,并设置了该线程的优先级。
步骤二、开启线程 mHandlerThread.start();
源码分析:
//Causes this thread to begin execution; the Java Virtual Machine
//calls the run method of this thread.
public synchronized void start() {...}
//调用mHandlerThread的start(),最终将调用mHandlerThread的run()方法。
@Override
public void run() {
mTid = Process.myTid();//获取当前线程的ID
Looper.prepare();//创建Looper和MessageQueue
//通过锁机制,获取当前线程的Looper对象
synchronized (this) {
//获取当前线程的Looper对象
mLooper = Looper.myLooper();
//发送通知,已经获取当前线程的Looper对象
//主要是在第三步创建Handler中的mHandlerThread.getLooper()内使用
notifyAll();
}
Process.setThreadPriority(mPriority);//设置线程的优先级
//主要是做一些开启消息循环前的准备工作 -->>分析1
onLooperPrepared();
//开启消息循环
Looper.loop();
mTid = -1;
}
分析1: onLooperPrepared()
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
总结:
第一步创建了线程,第二步开启该线程。开启线程时,会创建Looper和MessageQueue,成功后线程会进入消息循环,不断从消息队列中取出消息并分发消息。
步骤三、创建Handler,并重写handleMessage()方法
使用方法:
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//对信息的相关处理操作
//在子线程mHandlerThread中运行
super.handleMessage(msg);
}
};
//创建Handler时传入Looper对象,此时Handler就与该Looper绑定,进而与Looper所在的线程也绑定
//获取当前线程(即mHandlerThread线程)的Looper对象
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 {
//等待,直到mHandlerThread的run()方法创建了Looper
//并 notifyAll()发送通知
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
总结:
复写handleMessage()方法,该方法是在子线程mHandlerThread中运行 ,而非主线程,要注意。因为是在子线程中,所以可以进行一些耗时相关的操作。另外,通过Handler机制,可以实现该子线程与主线程的通信,进而更新UI。
步骤四、创建消息并发送消息
//在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//与Handler中的使用方法一样,就不多做介绍了。
步骤五、结束线程
//之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
//源码分析:mHandlerThread.quit();
public boolean quit() {
//获取当前Looper
Looper looper = getLooper();
if (looper != null) {
//调用Looper的quit()方法 -->>分析1
looper.quit();
return true;
}
return false;
}
//分析1: looper.quit();
public void quit() {
//调用MessageQueue的quit()方法 -->>分析2
mQueue.quit(false);
}
//通过looper.quit()退出是一种不安全的退出方法,
//还有一种安全的退出方法,即looper.quitSafely();
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();//-->>分析a
return true;
}
return false;
}
//分析a looper.quitSafely()的源码实现:
public void quitSafely() {
//通过源码,截至到目前可以看出,安全不安全主要是mQueue.quit()方法
//内的boolean变量是true还是false
mQueue.quit(true);// -->>分析2
}
分析2: mQueue.quit(false);
void quit(boolean safe) {
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {//安全的退出方式
removeAllFutureMessagesLocked();// -->>分析4(请先看分析3)
} else {//非安全的退出方式
removeAllMessagesLocked();// -->>分析3
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
//分析3、 removeAllMessagesLocked()
//不管该消息是否在使用,把消息队列中的所有消息都回收
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
//Recycles a Message that may be in-use.
//这里,不管Message是否在使用,都回收销毁,
//所以也就决定了它肯定是不安全的
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//分析4、 removeAllFutureMessagesLocked()
//首先判断是否有消息再使用中,如果没有就按照分析3的方法全部回收,
//如果有消息正在使用中,这个使用的消息不做回收,它依然能够正常执行完成,
//而其他的所有的没有正在执行的全部回收
private void removeAllFutureMessagesLocked() {
//Returns milliseconds since boot, not counting time spent in deep sleep.
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做了全部分析,从原理、使用实例,到源码分析,希望对您有帮助。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.****.net/haovin/article/details/89787721