HandlerThread
参考: 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 {
这样(参考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); } } }