您的位置:首页 > 其它

HandlerThread

2019-04-24 21:03 211 查看

参考: https://github.com/LRH1993/android_interview/blob/master/android/basis/HandlerThread.md

一、概述

HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它不仅建立了一个线程,并且创立了消息队列,有自己的looper,可以让我们在自己的线程中分发和处理消息,并对外提供自己这个Looper对象的get方法。

HandlerThread自带Looper使他可以通过消息队列,来重复使用当前线程,节省系统资源开销。这是它的优点也是缺点,每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

 

二、HandlerThread的使用

1.使用步骤

(1)创建HandlerThread的实例对象

private HandlerThread handlerThread;
handlerThread = new HandlerThread("stud");

该参数表示线程的名字,可以随便选择。

(2)启动我们创建的HandlerThread线程

handlerThread.start();

(3)将我们的handlerThread与Handler绑定在一起。 还记得是怎样将Handler与线程对象绑定在一起的吗?其实很简单,就是将线程的looper与Handler绑定在一起,代码如下:

threadHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
threadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
return true;//返回true不走handler的handlemessage方法
}
}) {
@Override
public void handleMessage(Message msg) {
Log.d("handlerThreadActivity", handlerThread.getName() + "");
}
};

完整代码如下:

public class HandlerThreadActivity extends Activity {
private static final int MSG_UPDATE_INFO = 0x100;
private Handler mMainHandler = new Handler();
private TextView textView;
private Button button;
private Handler threadHandler;
private HandlerThread handlerThread;
private boolean isUpdate = false;
Message message = new Message();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_btn);
textView = (TextView) findViewById(R.id.tv_task);
button = (Button) findViewById(R.id.btn_task);
initHandlerThread();
}

private void initHandlerThread() {
handlerThread = new HandlerThread("stud");
handlerThread.start();
threadHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
threadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
return true;
}
}) {
@Override
public void handleMessage(Message msg) {
Log.d("handlerThreadActivity", handlerThread.getName() + "");
}
};

button.setOnClickListener(v -> {
if (!isUpdate) {                threadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
} else {
threadHandler.removeMessages(MSG_UPDATE_INFO);
}
isUpdate = !isUpdate;
});

message.what = MSG_UPDATE_INFO;
}

//模拟服务器解析数据
private void checkForUpdate() {
try {
Thread.sleep(200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前股票行情:<font color='red'>%d</font>";
result = String.format(result, (int) (Math.random() * 5000 + 1000));
textView.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
mMainHandler.removeCallbacksAndMessages(null);
}

public static void start(Context context) {
Intent intent = new Intent(context, HandlerThreadActivity.class);
context.startActivity(intent);
}
}

 

三、HandlerThread原理:

HandlerThread的实现原理比较简单,源码也比较少(只有150行代码);

/**
* 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.
*/
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 needed 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对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}

public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
// If the thread has been started, wait until the looper has been created.
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;
}
}

我们来逐一解读:

public class HandlerThread extends Thread {
(1)它完全可以当一个线程来使用,就像上面我们说的:new HandlerTread().start()这样,但是在子线程中创建一个Handler,需要手动创建Looper,即先Looper.parpare(),完了再Looper.loop()
这样(参考handler原理解析),我们来看看HandlerThread中的构造方法
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方法

@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}

使用HandlerThread的时候必须调用

start()
方法,接着才可以将我们的HandlerThread和我们的handler绑定在一起,是因为我们是在
run()
方法才开始初始化我们的looper,而我们调用HandlerThread的
start()
方法的时候,线程会交给虚拟机调度,由虚拟机自动调用run方法:

handlerThread.start();
threadHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
threadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
return true;
}
})....................

这里我们为什么要使用锁机制和

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 previously 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);
}
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: