Android 多线程之HandlerThread介绍
一、什么是HandlerThread及特点
HandlerThread本质就是一个Thread,在内部建立了消息队列机制(Looper、MessageQueue),具有消息循环的线程。
特点
HandlerThread本质上一个线程类,它继承了Thread HandlerThread与自己的内部Looper对象,可以进行loop循环 通过获取looper对象传递给Handler对象,并可以在Handler的handleMessage方法中执行异步任务 与线程池重并发不同,handlerThread是一个串行队列,背后只有一个线程 优点就是不会堵塞,减少对信性能的消耗,缺点是不能同时进行多任务处理,需要等待处理,效率较低。二、HandlerThread使用方法
1.创建HandlerThread线程,并在构造方法内传入线程名称,可以是任意字符串
HandlerThread mHandlerThread = new HandlerThread("Handler_Thread");
2.启动线程
mHandlerThread.start();
3.创建异步Handler对象,并构建Callback消息循环机制
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //子线程内运行,此处不能更新UI Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName()); // mTvContent.setText(String.valueOf(msg.what)); //主线程Hanlder,发送消息通知更新UI mUIHandler.sendMessage(msg); return false; } });
以上步骤3构建一个完整异步Handler,将HandlerThread的Looper对接和Callback接口类作为参数传递到异步 mchildHandler,这样当前的mchildHandler对象就拥有HandlerThread的Looper对象,由于HandlerThread本身是异步线程,故Looper也绑定到异步线程中,所以handleMessage方法是处理异步耗时任务,当处理状态发生改变,需要UI线程的Handler发送消息通知更新UI。
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //子线程内运行,此处不能更新UI Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName()); // mTvContent.setText(String.valueOf(msg.what)); //主线程Hanlder,通知主线程更新 Message message = Message.obtain(); message.what=msg.what; mUIHandler.sendMessage(message); return false; } });
模拟触发异步线程
for (int i = 0; i <= 10; i++) { Message obtain = Message.obtain(); obtain.what = i; mchildHandler.sendMessageAtTime(obtain,1000); }
源码分析">三、HandlerThread源码分析
首先我们从构造函数开始:
public class HandlerThread extends Thread { //线程优先级 int mPriority; int mTid = -1; //当前线程所持有的Looper对象 Looper mLooper; private @Nullable Handler mHandler; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } /** * Constructs a HandlerThread. * @param name * @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread. */ public HandlerThread(String name, int priority) { super(name); mPriority = priority; }
从源码可以看出HandlerThread是继承于Thread,本质上是一个线程,构造方法需要传入两个参数,一个是name指的是线程名称,一个是priority指的是线程的优先级,当创建了HandlerThread对象并开启线程start方法后,run方法就会马上执行,接下来看看run方法:
/** * 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对象 Looper.prepare(); synchronized (this) { //把初始化的Looper对象赋值给HandlerThread的内部mLooper mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
从源码可以看出,当run方法被执行后,会调用Looper.prepare()创建一个Looper对象和MessageQueue对象并绑定了在当前线程(异步线程);这样才可以Looper对象以参数的形式传给Handler对象,从而确保Handler的hanldMessage方法是在子线程(异步线程)中执行的;接下来会把创建的Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll方法唤醒等待线程,最后调用loop方法,中间有个onLooperPrepared方法,可以重写此方法,也就是说在loop开启循环之前,可以做一些其他的操作。有个疑问就是在loop开启循环之前,为什么要唤醒等待线程呢,接下来看看getLooper方法:
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason 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 . 返回此线程关联的Looper对象 */ public Looper getLooper() { //如果线程没有开启,则返回null if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. //如果线程被开启了,而且当前线程Looper为null时,此时会等待状态,直到mLooper被初始化并赋值后会被唤醒 synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
当外部主线程调用getLooper方法,先会判断当前异步线程是否启动了,如果isAlive()为false ,说明线程未开启,会返回null,如果线程启动了,则会进入同步代码块内并判断mLooper是否为null,也就是内部变量mLooper是否被赋值,为null表示未赋值尚未初始化,进入等待状态,直到mLooper被赋值后,notifyAll方法会唤醒所以的等待线程 ,最后返回Looper对象。 之所以有个等待唤醒机制,主要getLooper方法是在主线程调用的,而mLooper是在子线程初始化赋值的,也就是我们无法保证不同线程访问同一个对象 是否被创建了,也就是说在调用getLooper方法来获取Looper对象时,无法确保mLooper已经被创建了 ,,故引入等待唤醒机制来确保线程创建开启后 并且Looper对象 也被创建后才可以成功返回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; }
线程的退出提供两个方法,一个quit,一个quitSafely,quitSafely相对更安全下。
quit:会清除MessageQueue中全部的消息,无论是延迟还是非延迟的消息都清除。
quitSafely:该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环。
上一篇: PHP设计模式、工厂模式讲解