HandlerThread使用

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 }
View Code

①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 }
View Code
原文地址:https://www.cnblogs.com/ganchuanpu/p/7582372.html