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

相关推荐
夫唯不争,故无尤也7 分钟前
Tomcat 启动后只显示 index.jsp,没有进入你的 Servlet 逻辑
java·servlet·tomcat
zz-zjx14 分钟前
Tomcat核心组件全解析
java·tomcat
Deschen17 分钟前
设计模式-外观模式
java·设计模式·外观模式
大熊的瓜地37 分钟前
Android automotive 框架
android·android car
lly2024061 小时前
SQL ROUND() 函数详解
开发语言
大宝剑1701 小时前
python环境安装
开发语言·python
why技术1 小时前
从18w到1600w播放量,我的一点思考。
java·前端·后端
lly2024061 小时前
CSS3 多媒体查询
开发语言
夫唯不争,故无尤也1 小时前
JavaWeb流式传输速查宝典
java·流式传输