初次看到HandlerThread的名字,我们可能会联想到Handler和Thread这两个类,没错,它其实就是跟Handler和Thread有莫大的关系。HandlerThread继承自Thread,它本质上就是一个Thread,而且专门用来处理Handler的消息。
看看官方对它的解释:
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可以创建一个带有looper的线程,looper对象可以用于创建Handler类来进行来进行调度,而且start()方法必须被调用。
在Android开发中,不熟悉多线程开发的人一想到要使用线程,可能就用new Thread(){…}.start()这样的方式。实质上在只有单个耗时任务时用这种方式是可以的,但若是有多个耗时任务要串行执行呢?那不得要多次创建多次销毁线程,这样导致的代价是很耗系统资源,容易存在性能问题。那么,怎么解决呢?
我们可以只创建一个工作线程,然后在里面循环处理耗时任务,创建过程如下:
Handler mHandler; private void createWorkerThread() { new Thread() { @Override public void run() { super.run(); Looper.prepare(); mHandler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { ...... } }; Looper.loop(); } }.start(); }
在该工作线程中,
- 调用Looper.prepare()创建与当前线程绑定的Looper实例;
- 使用上面创建的Looper生成Handler实例;
- 调用Looper.loop()实现消息循环;
然后透过Looper的循环,在Handler的handlerMessage()中进行异步任务的循环处理。而这也正好是HandlerThread的实现。
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
那么我们看下HandlerThread有哪些特点:
- HandlerThread本质上是一个线程类,它继承了Thread;
- HandlerThread有自己的内部Looper对象,通过Looper.loop()进行looper循环;
- 通过获取HandlerThread的looper对象传递给Handler对象,然后在handleMessage()方法中执行异步任务;
- 创建HandlerThread后必须调用HandlerThread.start()方法来启动线程。
HandlerThread handlerThread = new HandlerThread("Handler Thread"); //HandlerThread handlerThread = new HandlerThread("Handler Thread",Process.THREAD_PRIORITY_DEFAULT);
HandlerThread默认有两个构造函数,提供了线程名参数和线程优先级参数的设置。
handlerThread.start();
通过start()方法就可以启动一个HandlerThread了,该线程会不断地循环运行。
Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //运行在工作线程(子线程)中,用于实现自己的消息处理 return true; } });
通过将HandlerThread绑定的Looper对象传递给Handler作为参数,构建一个异步的Handler对象,为了能实现耗时任务的异步执行,我们重写了Handler的Callback接口的handleMessage()方法,当然也可以不重写该方法,而通过post()方法进行耗时任务操作。
Handler workderHandler = new Handler(handlerThread.getLooper()); workderHandler.post(new Runnable() { @Override public void run() { //运行在工作线程(子线程)中,用于实现自己的消息处理 } });
最后,我们就可以通过调用workerHandler以发送消息的形式发送耗时任务到工作线程HandlerThread中去执行,实际上就是在Handler.Callback里的handleMessage()中执行。
这里要注意,在创建Handler作为HandlerThread线程消息执行者的时候必须先调用start()方法,因为创建Handler所需要的Looper参数是从HandlerThread中获得的,而Looper对象的赋值又是在HandlerThread的run()方法中创建。
import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by Administrator on 2016/9/18. */ public class HandlerThreadActivity extends Activity implements Handler.Callback { private DBHandlerThread mDBHandlerThread; private Handler mUIHandler; //与UI线程相关联的Handler Button mBtnQuery; TextView mTextResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnQuery = (Button) findViewById(R.id.buttonQuery); mTextResult = (TextView) findViewById(R.id.result); mBtnQuery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //将异步耗时任务发送到HandlerThread中 //Message msg = Message.obtain(null, DBHandlerThread.MSG_QUERY_FRIENDS); //mDBHandlerThread.getWorkerHandler().sendMessage(msg); mDBHandlerThread.queryFriends(); } }); mUIHandler = new Handler(this); initWorkerThread(); } protected void initWorkerThread() { mDBHandlerThread = new DBHandlerThread("Handler Thread"); mDBHandlerThread.setUIHandlerCallBack(mUIHandler); mDBHandlerThread.start(); //start()后会执行Thread的run()方法 } @Override protected void onDestroy() { super.onDestroy(); mDBHandlerThread.setUIHandlerCallBack(null); mDBHandlerThread.quit(); mDBHandlerThread = null; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case DBHandlerThread.MSG_QUERY_FRIENDS: // update UI break; } return false; } }
import android.os.Handler; import android.os.HandlerThread; import android.os.Message; /** * Created by Administrator on 2016/9/18. */ public class DBHandlerThread extends HandlerThread implements Handler.Callback { public static final int MSG_QUERY_FRIENDS = 100; private Handler mWorkerHandler; //与工作线程相关联的Handler private Handler mUIHandler; //与UI线程相关联的Handler public DBHandlerThread(String name) { super(name); } public DBHandlerThread(String name, int priority) { super(name, priority); } public void setUIHandlerCallBack(Handler handler) { this.mUIHandler = handler; } public Handler getWorkerHandler() { return mWorkerHandler; } @Override protected void onLooperPrepared() { mWorkerHandler = new Handler(getLooper(), this); } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_QUERY_FRIENDS: //...查询数据库操作... Message message = Message.obtain(null, MSG_QUERY_FRIENDS); mUIHandler.sendMessage(message); //通知UI更新 break; } return true; } public void queryFriends() { Message msg = Message.obtain(null, MSG_QUERY_FRIENDS); mWorkerHandler.sendMessage(msg); } }
HandlerThread的源码不多,先看下它的构造函数:
/** * 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; //当前线程id //当前线程持有的Looper对象 Looper mLooper; public HandlerThread(String name) { //调用父类默认的方法创建线程 super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } //带优先级参数的构造方法 public HandlerThread(String name, int priority) { super(name); mPriority = priority; } ............... }
从代码中得知,HandlerThread带有两个构造函数,可传递两个参数,一个参数是name,指的是线程的名称,另一个参数是priority,指的是线程优先级。线程的优先级的取值范围为-20到19。优先级高的获得的CPU资源更多,反之则越少。-20代表优先级最高,19最低。我们可以根据自己的需要去设置线程的优先级,也可以采用默认的优先级,HandlerThread的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为0。该优先级是再run()方法中设置的,我们看它的run()方法:
public class HandlerThread extends Thread { /** * 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(); //获得当前线程的id Looper.prepare(); //准备循环条件 //通过锁机制来获得当前线程的Looper对象 synchronized (this) { mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } //设置当前线程的优先级 Process.setThreadPriority(mPriority); //在线程循环之前做一些准备工作(子类可实现也可不实现) onLooperPrepared(); //启动loop Looper.loop(); mTid = -1; } }
run()方法主要是通过Looper.prepare()和Looper.loop()构造了一个循环线程。这里要注意,在创建HandlerThread对象后必须调用其start()方法才能进行run()方法体的执行。
在Looper.prepare()执行后,Looper对象会被创建,然后通过同步锁机制,将Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程。接着为线程赋予优先级,然后执行onLooperPrepared()方法,该方法是一个空实现,留给我们必要的时候去重写的,主要用来做一些初始化工作。最后通过执行Looper.loop()在线程中启动消息队列。
我们看到在run()方法中进行了唤醒等待线程,为什么要这么做呢?答案就在getLooper()方法中:
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The 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(); //等待,直到另一个线程调用notify()或notifyAll()来唤醒它 } catch (InterruptedException e) { } } } return mLooper; }
该方法用来获取当前子线程HandlerThread所关联的Looper对象实例。首先判断HandlerThread线程是否存活,如果没有存活就直接返回null,否则继续执行,进入同步块并判断Looper对象是否为空以及线程是否启动,若都满足,则调用wait()方法进入阻塞阶段,直到Looper对象被成功创建并且通过notifyAll()方法唤醒该等待线程,最后才返回该Looper对象。
Looper对象的创建是在run()方法进行的,也即在子线程中执行的,而getLooper()方法是在UI线程中调用的,若不使用等待唤醒机制,我们就无法保证在UI线程中调用getLooper()方法时Looper对象已经被创建,会面临一个同步的问题,所以HandlerThread就通过等待唤醒机制来解决该同步问题。