HandlerThread 是一个包含 Looper 的 Thread,我们可以直接使用这个 Looper 创建 Handler。
1.HandlerThread 源码
1 public class HandlerThread extends Thread { 2 int mPriority; 3 int mTid = -1; 4 Looper mLooper; 5 6 public HandlerThread(String name) { 7 super(name); 8 mPriority = Process.THREAD_PRIORITY_DEFAULT; 9 } 10 11 //也可以指定线程的优先级,注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级! 12 public HandlerThread(String name, int priority) { 13 super(name); 14 mPriority = priority; 15 } 16 17 // 子类需要重写的方法,在这里做一些执行前的初始化工作 18 protected void onLooperPrepared() { 19 } 20 21 //获取当前线程的 Looper 22 //如果线程不是正常运行的就返回 null 23 //如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify 24 public Looper getLooper() { 25 if (!isAlive()) { 26 return null; 27 } 28 29 synchronized (this) { 30 while (isAlive() && mLooper == null) { //循环等待 31 try { 32 wait(); 33 } catch (InterruptedException e) { 34 } 35 } 36 } 37 return mLooper; 38 } 39 40 //调用 start() 后就会执行的 run() 41 @Override 42 public void run() { 43 mTid = Process.myTid(); 44 Looper.prepare(); //帮我们创建了 Looepr 45 synchronized (this) { 46 mLooper = Looper.myLooper(); 47 notifyAll(); //Looper 已经创建,唤醒阻塞在获取 Looper 的线程 48 } 49 Process.setThreadPriority(mPriority); 50 onLooperPrepared(); 51 Looper.loop(); //开始循环 52 mTid = -1; 53 } 54 55 public boolean quit() { 56 Looper looper = getLooper(); 57 if (looper != null) { 58 looper.quit(); 59 return true; 60 } 61 return false; 62 } 63 64 public boolean quitSafely() { 65 Looper looper = getLooper(); 66 if (looper != null) { 67 looper.quitSafely(); 68 return true; 69 } 70 return false; 71 } 72 73 public int getThreadId() { 74 return mTid; 75 } 76 }
①HandlerThread 本质还是个 Thread,创建后别忘了调用 start()。
②在 run() 方法中创建了 Looper,调用 onLooperPrepared 后开启了循环
③我们要做的就是在子类中重写 onLooperPrepared,做一些初始化工作
④在创建 HandlerThread 时可以指定优先级,注意这里的参数是 Process.XXX 而不是 Thread.XXX
2.HandlerThread 的使用场景
我们知道,HandlerThread 所做的就是在新开的子线程中创建了 Looper,那它的使用场景就是 Thread + Looper 使用场景的结合,即:在子线程中执行耗时的、可能有多个任务的操作。
比如说多个网络请求操作,或者多文件 I/O 等等。
使用 HandlerThread 的典型例子就是 IntentService,我们下篇文章介绍它。
3.举个栗子
我们写一个使用 HandlerThread 实现子线程完成多个下载任务的 demo。
先创建一个 HandlerThread 子类,它有两个 Handler 类型的成员变量,一个是用于在子线程传递、执行任务,另一个用于外部传入,在主线程显示下载状态:
1 /** 2 * 继承 HandlerThread 模拟下载线程 3 */ 4 public class DownloadThread extends HandlerThread implements Handler.Callback { 5 6 private final String TAG = this.getClass().getSimpleName(); 7 private final String KEY_URL = "url"; 8 public static final int TYPE_START = 1; 9 public static final int TYPE_FINISHED = 2; 10 11 private Handler mWorkerHandler; 12 private Handler mUIHandler; 13 private List<String> mDownloadUrlList; 14 15 public DownloadThread(final String name) { 16 super(name); 17 } 18 19 @Override 20 protected void onLooperPrepared() { //执行初始化任务 21 super.onLooperPrepared(); 22 mWorkerHandler = new Handler(getLooper(), this); //使用子线程中的 Looper 23 if (mUIHandler == null) { 24 throw new IllegalArgumentException("Not set UIHandler!"); 25 } 26 27 //将接收到的任务消息挨个添加到消息队列中 28 for (String url : mDownloadUrlList) { 29 Message message = mWorkerHandler.obtainMessage(); 30 Bundle bundle = new Bundle(); 31 bundle.putString(KEY_URL, url); 32 message.setData(bundle); 33 mWorkerHandler.sendMessage(message); 34 } 35 } 36 37 public void setDownloadUrls(String... urls) { 38 mDownloadUrlList = Arrays.asList(urls); 39 } 40 41 public Handler getUIHandler() { 42 return mUIHandler; 43 } 44 45 //注入主线程 Handler 46 public DownloadThread setUIHandler(final Handler UIHandler) { 47 mUIHandler = UIHandler; 48 return this; 49 } 50 51 /** 52 * 子线程中执行任务,完成后发送消息到主线程 53 * 54 * @param msg 55 * @return 56 */ 57 @Override 58 public boolean handleMessage(final Message msg) { 59 if (msg == null || msg.getData() == null) { 60 return false; 61 } 62 63 String url = (String) msg.getData().get(KEY_URL); 64 65 66 //下载开始,通知主线程 67 Message startMsg = mUIHandler.obtainMessage(TYPE_START, " 开始下载 @" + DateUtils.getCurrentTime() + " " + url); 68 mUIHandler.sendMessage(startMsg); 69 70 SystemClock.sleep(2000); //模拟下载 71 72 //下载完成,通知主线程 73 Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, " 下载完成 @" + DateUtils.getCurrentTime() + " " + url); 74 mUIHandler.sendMessage(finishMsg); 75 76 return true; 77 } 78 79 @Override 80 public boolean quitSafely() { 81 mUIHandler = null; 82 return super.quitSafely(); 83 } 84 }
可以看到,DownloadThread 中做了以下工作:
创建一个子线程 Handler
然后在 onLooperPrepared()中初始化 Handler,使用的是 HandlerThread 创建的 Looper
同时将外部传入的下载 url 以 Message 的方式发送到子线程中的 MessageQueue 中
这样当调用 DownloadThread.start() 时,子线程中的 Looper 开始工作,会按顺序取出消息队列中的队列处理,然后调用子线程的 Handler 处理
也就是上面的 handleMessage() 方法,在这个方法中进行耗时任务
然后通过 mUIHandler 将下载状态信息传递到主线程
调用 Activity 的代码:
1 public class HandlerThreadActivity extends AppCompatActivity implements Handler.Callback { 2 3 @BindView(R.id.tv_start_msg) 4 TextView mTvStartMsg; 5 @BindView(R.id.tv_finish_msg) 6 TextView mTvFinishMsg; 7 @BindView(R.id.btn_start_download) 8 Button mBtnStartDownload; 9 10 private Handler mUIHandler; 11 private DownloadThread mDownloadThread; 12 13 @Override 14 protected void onCreate(@Nullable final Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_handler_thread_test); 17 ButterKnife.bind(this); 18 init(); 19 } 20 21 private void init() { 22 mUIHandler = new Handler(this); 23 mDownloadThread = new DownloadThread("下载线程"); 24 mDownloadThread.setUIHandler(mUIHandler); 25 mDownloadThread.setDownloadUrls("http://pan.baidu.com/s/1qYc3EDQ", 26 "http://bbs.005.tv/thread-589833-1-1.html", "http://list.youku.com/show/id_zc51e1d547a5b11e2a19e.html?"); 27 } 28 29 @OnClick(R.id.btn_start_download) 30 public void startDownload() { 31 mDownloadThread.start(); 32 mBtnStartDownload.setText("正在下载"); 33 mBtnStartDownload.setEnabled(false); 34 } 35 36 //主线程中的 Handler 处理消息的方法 37 @Override 38 public boolean handleMessage(final Message msg) { 39 switch (msg.what) { 40 case DownloadThread.TYPE_FINISHED: 41 mTvFinishMsg.setText(mTvFinishMsg.getText().toString() + " " + msg.obj); 42 break; 43 case DownloadThread.TYPE_START: 44 mTvStartMsg.setText(mTvStartMsg.getText().toString() + " " + msg.obj); 45 break; 46 } 47 return true; 48 } 49 }