版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/79028368
一、使用场景
举个例子,数据实时更新,我们每10秒需要切换一下显示的数据,如果我们将这种长时间的反复调用操作放到UI线程中,虽说可以执行,但是这样的操作多了之后,很容易会让UI线程卡顿甚至崩溃。 于是,就必须在子线程中调用这些了。 HandlerThread继承自Thread,一般适应的场景,便是集Thread和Handler之所长,适用于会长时间在后台运行,并且间隔时间内(或适当情况下)会调用的情况,比如上面所说的实时更新。
二、HandlerThread用法
//步骤1:创建HandlerThread的实例对象=已经创建了一个新线程
//参数=线程名字,作用是标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
//步骤2:启动线程
mHandlerThread.start();
//步骤3:创建工作线程Handler,实现消息处理的操作,并与其他线程进行通信
Handler mHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
//消息处理
return true;
}
});
//步骤4:结束线程,即停止线程的消息循环
mHandlerThread.quit();
三、HandlerThread类的源码
public class HandlerThread extends Thread {
//线程优先级
int mPriority;
//当前线程id
int mTid = -1;
//当前线程持有的Looper对象
Looper mLooper;
/*分析1:如何创建新线程*/
//HandlerThread在进行实例化的同时,已经创建了一个新线程,这说明猫腻是在构造方法
//HandlerThread类有两个构造方法
//不同之处就是设置当前线程的优先级参数。你可以根据自己的情况来设置优先级,也可以使用默认优先级。
//方法1. 默认优先级
public HandlerThread(String name) {
//通过调用父类默认的方法创建线程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//方法2. 根据需求设置优先级-构造方法带优先级参数
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/*分析2:如何创建Handler*/
//步骤1:通过run()方法作好创建Handler的准备
@Override
public void run() {
//获得当前线程的id
mTid = Process.myTid();
//创建了一个Looper对象并初始化了一个MessageQueue
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//通知getLooper方法中的wait当前线程已经创建mLooper对象成功,让wait结束等待
notifyAll();
}
//设置当前线程的优先级
Process.setThreadPriority(mPriority);
//在线程循环之前做一些准备工作
//该方法实现体是空的,子类可以实现该方法,也可以不实现。
onLooperPrepared();
//进行消息循环,即不断从MessageQueue中取消息和派发消息
Looper.loop();
mTid = -1;
}
}
//线程循环前的准备工作
protected void onLooperPrepared() {
}
//步骤2:在创建Handler时调用了getLooper()
//Handler mHandler = new Handler( handlerThread.getLooper())
//getLooper()的作用主要是获得当前HandlerThread线程中的mLooper对象
public Looper getLooper() {
//如果线程不是存活的,则直接返回null
if (!isAlive()) {
return null;
}
synchronized (this) {
//首先判断当前线程是否存活
//如果不是存活的,这直接返回null。
//其次如果当前线程存活的,在判断线程的成员变量mLooper是否为null,如果为null,说明当前线程已经创建成功,但是还没来得及创建Looper对象
while (isAlive() && mLooper == null) {
try {
//因此,这里会调用wait方法去等待
wait();
} catch (InterruptedException e) {
}
}
}
//当run方法中的notifyAll方法调用之后通知当前线程的wait方法结束等待并跳出循环
//最终getLooper()返回的是我们在run方法中创建的mLooper
return mLooper;
}
/*分析3:结束新线程*/
//Handler有两种让当前线程退出循环的方法:quit() 和 quitSafely()
//第一种:效率高,但线程不安全
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;
}
- 在获得mLooper对象的时候存在一个同步的问题:只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,才能将创建的Handler与该Looper绑定
- 解决方案:在run()中成功创建Looper对象后,立即调用notifyAll()通知 getLooper()中的wait()结束等待,并返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.widget.TextView;
import com.zhy.blogcodes.R;
public class HandlerThreadActivity extends AppCompatActivity
{
private TextView mTvServiceInfo;
private HandlerThread mCheckMsgThread;
private Handler mCheckMsgHandler;
private boolean isUpdateInfo;
private static final int MSG_UPDATE_INFO = 0x110;
//与UI线程管理的handler
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread_handler);
//创建后台线程
initBackThread();
mTvServiceInfo = (TextView) findViewById(R.id.id_textview);
}
@Override
protected void onResume()
{
super.onResume();
//开始查询
isUpdateInfo = true;
mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause()
{
super.onPause();
//停止查询
isUpdateInfo = false;
mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);
}
private void initBackThread()
{
mCheckMsgThread = new HandlerThread("check-message-coming");
mCheckMsgThread.start();
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())
{
@Override
public void handleMessage(Message msg)
{
checkForUpdate();
if (isUpdateInfo)
{
mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
}
}
};
}
/**
* 模拟从服务器解析数据
*/
private void checkForUpdate()
{
try
{
//模拟耗时
Thread.sleep(1000);
mHandler.post(new Runnable()
{
@Override
public void run()
{
String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
result = String.format(result, (int) (Math.random() * 3000 + 1000));
mTvServiceInfo.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
protected void onDestroy()
{
super.onDestroy();
//释放资源
mCheckMsgThread.quit();
}
}
在onCreate中,去创建和启动了HandlerThread,并且关联了一个mCheckMsgHandler。然后我们分别在onResume和onPause中去开启和暂停我们的查询,最后在onDestory中去释放资源。
这样就实现了我们每隔5s去服务端查询最新的数据,然后更新我们的UI,当然我们这里通过Thread.sleep()模拟耗时,返回了一个随机数,大家可以很轻易的换成真正的数据接口。