HandlerThread(子线程也可以有消息传递机制)

知识点

HandlerThread,顾名思义,Handler+Thread,就是让子线程也能有一套和MainThread一样的Handler消息机制。
  • HandlerThread本质上是一个线程类,它继承了Thread
  • HandlerThread有自己的内部Looper对象,可以进行looper循环
  • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象
MainThread vs HandlerThread
MainThreadHandler的消息传递机制
  1. 用户启动一个应用,系统内部建立一个进程。
  2. 进程启动主线程Main Thread。
  3. Main Thread通过Looper建立一个消息队列Message Queue。
  4. 消息队列是存在于主线程中的,在主线程中开始无限循环。
  5. 每当有新的Message进来,消息队列就开始处理,如果没有,就执行return继续等待。
HandlerThread的消息传递机制
  1. 创建实例对象HandlerThread
  2. 启动HandlerThread的主线程Thread()
  3. 创建一个Handler,该Handler使用的是HandlerThread的looper,并传入一个自定义的的消息处理机制来处理
  4. 处理完毕调用MainThread创建的另外一个Handler去处理结果
区别:
  • 【HandlerThread(子线程也可以有消息传递机制)】平时使用Handler的时候系统内部已经帮我们创建好主线程和启动主线程了,而使用HandlerThread需要我们手动去创建一个属于HandlerThread的主线程并且去启动它
  • 使用Handler的时候使用的是HandlerThread的looper而不是MainThread的looper
  • 不使用handleMessage来处理,而是使用一个实现了Handler.Callback接口的自定义的处理方法
  • HandlerThread不能操作UI,所以处理完毕后还是需要使用主线程的Handler去处理UI
这个和直接在MainThread中new Thread的作用是一样的,只不过是HandlerThread比Thread多了一套和MainThread一样的消息处理机制,有利于串行地处理消息,不会造成并发。
如图所示:
HandlerThread(子线程也可以有消息传递机制)
文章图片

一个点击下载图片的例子
HandlerThread(子线程也可以有消息传递机制)
文章图片

package com.bourne.android_common.ServiceDemo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import com.bourne.android_common.R; import com.bourne.common_library.utils.Logout; import java.io.BufferedInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; public class HandlerThreadActivity extends AppCompatActivity { class ImageBean { private String url; private Bitmap bitmap; public String getUrl() { return url; }public void setUrl(String url) { this.url = url; }public Bitmap getBitmap() { return bitmap; }public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; } }/** * 图片地址集合 */ private String url[] = { "http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg", "http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg", "http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg", "http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg" }; private ImageView imageView; private HandlerThread handlerThread; private Thread loadImageThread; private int count = 0; /** * 处理UI */ Handler mainThreadHandler = new Handler() { @Override public void handleMessage(Message msg) { Logout.e("次数:" + msg.what); ImageBean imageBean = (ImageBean) msg.obj; imageView.setImageBitmap(imageBean.getBitmap()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); imageView = (ImageView) findViewById(R.id.imageView); createHandlerThread(); }/** * 通过HandlerThread的方式加载 * @param view */ public void loadByHandlerThread(View view) { Handler.Callback callBack = new loadImageCallBack(); Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), callBack); for (int i = 0; i < url.length; i++) { handlerThreadHandler.sendEmptyMessageDelayed(i, 1000 * i); } }/** * 通过Thread的方式加载 * @param view */ public void loadByThread(View view) { if (loadImageThread != null && !loadImageThread.isInterrupted()) { loadImageThread.interrupt(); } count = 0; loadImageThread = new Thread(new Runnable() { @Override public void run() { try { while (true) { Thread.sleep(1000); //在子线程中进行网络请求 Bitmap bitmap = downloadUrlBitmap(url[count]); ImageBean imageBean = new ImageBean(); imageBean.setBitmap(bitmap); imageBean.setUrl(url[count]); Message message = new Message(); message.what = count; message.obj = imageBean; count++; mainThreadHandler.sendMessage(message); //最后一张时停止加载 if (count >= url.length) { loadImageThread.interrupt(); } } } catch (InterruptedException e) { e.printStackTrace(); Logout.e("加载完毕,停止线程"); } } }); loadImageThread.start(); }/** * 处理下载图片 */ class loadImageCallBack implements Handler.Callback {@Override public boolean handleMessage(Message msg) { //在子线程中进行网络请求 Bitmap bitmap = downloadUrlBitmap(url[msg.what]); ImageBean imageBean = new ImageBean(); imageBean.setBitmap(bitmap); imageBean.setUrl(url[msg.what]); Message message = new Message(); message.what = msg.what; message.obj = imageBean; mainThreadHandler.sendMessage(message); return false; } }/** * 创建一个HandlerThread */private void createHandlerThread() { //创建实例对象 handlerThread = new HandlerThread("downloadImage"); handlerThread.start(); }/** * 下载图片的网络请求 * * @param urlString * @return */ private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap = BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap; }@Override protected void onDestroy() { super.onDestroy(); //释放资源 handlerThread.quit(); } }

可以看到我们在onCreate的时候创建了一个HandlerThread,并开启它的线程。点击按钮的时候创建一个Handler,取HandlerThread的Looper,并完成线程的操作。操作完成之后,发消息给UI线程并改变UI。
布局文件

源码解析
完整代码:
import android.os.Handler; import android.os.Looper; import android.os.Message; /** * */ public class HandlerThread extends Thread { /** * 线程优先级 */ int mPriority; int mTid = -1; /** * 当前线程持有的Looper对象 */ Looper mLooper; public MyHandlerThread(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 MyHandlerThread(String name, int priority) { super(name); mPriority = priority; }/** *必要时可以自己去重写 */ protected void onLooperPrepared() { }@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { //Looper对象将被创建 mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } //设置进程优先级 Process.setThreadPriority(mPriority); onLooperPrepared(); //开启looper循环语句 Looper.loop(); mTid = -1; }/** * 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; }synchronized (this) { while (isAlive() && mLooper == null) { try { //等待唤醒 wait(); } catch (InterruptedException e) { } } } return mLooper; }/** * Quits the handler thread's looper. * * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. *
* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *
* Using this method may be unsafe because some messages may not be delivered * before the looper terminates.Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. *
* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */ public boolean quit() { Looper looper = getLooper(); if (looper != null) { looper.quit(); return true; } return false; }/** * Quits the handler thread's looper safely. * * Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. *
* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *
* If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. *
* * @return True if the looper looper has been asked to quit or false if the * thread had not yet started running. */ 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; } }

/** * 线程优先级 */ int mPriority; int mTid = -1; /** * 当前线程持有的Looper对象 */ Looper mLooper; public MyHandlerThread(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 MyHandlerThread(String name, int priority) { super(name); mPriority = priority; }/** *必要时可以自己去重写 */ protected void onLooperPrepared() { }

这里初始化了Handler的对象,设置了优先级,mLooper是持有的Looper对象,onLooperPrepared是一个空实现,必要是可以自己重写。
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { //Looper对象将被创建 mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } //设置进程优先级 Process.setThreadPriority(mPriority); onLooperPrepared(); //开启looper循环语句 Looper.loop(); mTid = -1; }

run()方法中调用了Looper.prepare(),Loop.loop(),prepare()负责创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。
loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
/** * 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; }synchronized (this) { while (isAlive() && mLooper == null) { try { //等待唤醒 wait(); } catch (InterruptedException e) { } } } return mLooper; }

Looper的创建是在子线程中执行的,而Handler通过getLooper去获取mLooper的时候又是在主线程,怎么保证它们可以同步呢,原因是通过唤醒机制:
getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果启动了会判断Looper对象有没有被创建,如果都都没有满足则会继续等待,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,返回mLooper对象给Handler使用。
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方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
??
??当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
??
参考文章
  • Android 多线程之HandlerThread 完全详解
  • Android HandlerThread 完全解析

    推荐阅读