有时候我们需要在应用程序中创建一些
常驻的子线程不定期地执行一些计算型任务
,这时候可以考虑使用HandlerThread,它具有
创建带消息循环的子线程
的作用。
可通过构造器注入线程优先级,默认优先级为Process.THREAD_PRIORITY_DEFAULT
核心逻辑为run方法(复写Thread类的run方法):
外界可通过getLooper方法获取Looper对象:
然后通过Looper.myLooper方法返回与本线程绑定的Looper,正是刚创建的Looper:
Looper.loop方法将启动消息循环,不断从其内部封装的消息队列MessageQueue中取出消息,交由Handler执行。
一、HanderThread使用示例
先熟悉下HandlerThread的一般用法。我们创建一个如下所示的Activity:
- package com.example.handlethreaddemo;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.HandlerThread;
- import android.os.Looper;
- import android.os.Message;
- import android.util.Log;
- public class MainActivity extends Activity
- {
- private Looper mLooper;
- private MyHandler mHandler;
- private static final String TAG = "MainActivity";
- private static class MyHandler extends Handler
- {
- public MyHandler(Looper looper)
- {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg)
- {
- switch (msg.what)
- {
- case 1:
- Log.i(TAG, "当前线程是"+Thread.currentThread().getName()+",TEST 1");
- break;
- case 2:
- Log.i(TAG, "TEST 2");
- break;
- }
- }
- }
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //创建HandlerThread对象
- HandlerThread myHandleThread = new HandlerThread("HandlerThread<子线程>");
- //启动HandlerThread---->内部将启动消息循环
- myHandleThread.start();
- //获取Looper
- mLooper = myHandleThread.getLooper();
- //构造Handler,传入子线程中的Looper
- mHandler = new MyHandler(mLooper);
- /*
- * 注:经过上述步骤,Handler将绑定子线程的Looper和MessageQueue.
- * 也就是说handleMessage最终由子线程调用
- * */
- mHandler.sendEmptyMessage(1);
- Log.i(TAG,"当前线程是:"+Thread.currentThread().getName());
- }
- }
使用HandlerThread内部提供的Looper对象构造Handler对象,然后在ui线程中向Handler发送消息。log日志如下:
可见发送消息的线程为UI线程,而处理消息的线程为子线程,也就是说,我们在
子线程中创建了消息循环。
一般情况下,我们总是在UI线程中创建Handler对象,并使用界面组件提供的默认Looper,这个Looper绑定在UI线程上。所以我们在线程中向Handler发送消息时,最终的处理是在主线程中进行的。但正如开篇所说,我们有时需要构建常驻的子线程以不定期执行计算型任务,这时在子线程中创建消息循环将非常有用。
二、HandlerThread源码剖析
HandlerThread源码十分精简。
HandlerThread继承自java.lang.Thread,并封装了Looper对象:
- int mPriority;//优先级
- int mTid = -1;//线程标志
- Looper mLooper;//消息循环
可通过构造器注入线程优先级,默认优先级为Process.THREAD_PRIORITY_DEFAULT
- public HandlerThread(String name, int priority) {
- super(name);
- mPriority = priority;
- }
核心逻辑为run方法(复写Thread类的run方法):
- public void run() {
- mTid = Process.myTid();
- Looper.prepare();//创建Looper对象
- synchronized (this) {
- mLooper = Looper.myLooper();//获取与本线程绑定的Looper
- notifyAll();
- }
- Process.setThreadPriority(mPriority);
- onLooperPrepared();//回调接口,默认为空实现。
- Looper.loop();//启动消息循环--->may be blocked
- mTid = -1;
- }
外界可通过getLooper方法获取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 {
- wait();//异步等待Looper准备好
- } catch (InterruptedException e) {
- }
- }
- }
- return mLooper;
- }
如果调用getLooper方法时,Looper未准备好,那么将会阻塞线程,直到准备好Looper对象。
外界可调用quit方法终止消息循环:
- public boolean quit() {
- Looper looper = getLooper();
- if (looper != null) {
- looper.quit();//内部调用looper类的quit
- return true;
- }
- return false;
- }
附:
Looper类相信大家都不陌生,这里顺便简单提下(之前写过Handler和Looper):
Looper.prepare方法将会创建一个Looper对象(Looper类的构造器为私有,不可new),并将其放到ThreadLocal中,意为线程局部变量:
- public static void prepare() {
- prepare(true);
- }
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
然后通过Looper.myLooper方法返回与本线程绑定的Looper,正是刚创建的Looper:
- public static Looper myLooper() {
- return sThreadLocal.get();
- }
Looper.loop方法将启动消息循环,不断从其内部封装的消息队列MessageQueue中取出消息,交由Handler执行。
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- final MessageQueue queue = me.mQueue;
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " +
- msg.callback + ": " + msg.what);
- }
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- Log.wtf(TAG, "Thread identity changed from 0x"
- + Long.toHexString(ident) + " to 0x"
- + Long.toHexString(newIdent) + " while dispatching to "
- + msg.target.getClass().getName() + " "
- + msg.callback + " what=" + msg.what);
- }
- msg.recycle();
- }
- }
没有消息时,消息队列会阻塞。
以上就是HandlerThread的全部内容。
转自:http://blog.csdn.net/chdjj/article/details/39063557