android-handlerThread

记住一点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.csdn.net/haovin/article/details/89787721

相关推荐
码老白1 分钟前
【老白学 Java】Warshipv2.0(二)
java·网络
苹果酱05672 分钟前
前端面试vue篇:Vue2 和 Vue3 在设计和性能上有显著区别
java·spring boot·毕业设计·layui·课程设计
LKID体12 分钟前
Python操作neo4j库py2neo使用之py2neo 删除及事务相关操作(三)
开发语言·python·neo4j
小屁孩大帅-杨一凡14 分钟前
Python-flet实现个人视频播放器
开发语言·python·音视频
万琛14 分钟前
【java-Neo4j 5开发入门篇】-最新Java开发Neo4j
java·neo4j
算家云16 分钟前
快速识别模型:simple_ocr,部署教程
开发语言·人工智能·python·ocr·数字识别·检测模型·英文符号识别
Thomas_Cai28 分钟前
Python后端flask框架接收zip压缩包方法
开发语言·python·flask
霍先生的虚拟宇宙网络30 分钟前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing32 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript