• 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
  • 相关阅读:
    在项目中运用到的导航高亮
    【转载】IE8 inlineblock容器不撑开问题(利用重绘解决)
    我的博客正式开通
    【转载】响应式网页设计的9条基本原则
    一款不错的在线SVG制作工具
    【转载】前端不为人知的一面前端冷知识集锦
    11.3 Daily Scrum
    11.11 Daily Scrum
    11.7 Daily Scrum(周末暂停两天Daily Scrum)
    11.12 Daily Scrum(保存草稿后忘了发布·····)
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/7582372.html
Copyright © 2020-2023  润新知