Android多线程之HandlerThread

作者: 饱醉豚我去年买了个表 | 来源:发表于2017-07-28 14:11 被阅读1079次

    @author:小马快跑
    @email:mqcoder90@gmail.com
    @github:https://github.com/crazyqiang


    HandlerThread的介绍及用法

    HandlerThread继承自Thread,内部实现了初始化了Looper,并创建了消息队列,接着调用了Looper.loop()开启了消息循环,这样HandlerThread就可以处理通过Handler传递过来的Message了,因为HandlerThread中的run方法是无限循环,当有消息过来时处理消息,没有消息时就会阻塞。当明确不需要HandlerThread时,可以调用quit或者quitSafely (API 18以上使用)来尝试终止线程。

    先看实现的一个效果图:

    HandlerThread.gif
    完整代码已上传github:Android多线程之HandlerThread

    来分析实现代码,先定义对象:

     private HandlerThread handlerThread;
     private Handler mainHandler;
     private Handler uiHandler;
    

    初始化:

     handlerThread = new HandlerThread("handler_thread");
     handlerThread.start();
     //在主线程初始化,传入的是HandlerThread中的Looper
     mainHandler = new Handler(handlerThread.getLooper()) {
         @Override
         public void handleMessage(Message msg) {
             //Handler是在HandlerThread所在的子线程线程中处理Message
             switch (msg.what) {
                 case MSG_UPDATE:
                     if (isStop) return;
                     try {
                         //更新UI
                         String result = String.valueOf(new Random().nextInt(9000) + 1000);
                         sendMsg(UI_UPDATE, result, uiHandler);
                         //延迟2秒
                         Thread.sleep(2000);
                         //循环发送消息
                         mainHandler.sendEmptyMessage(MSG_UPDATE);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     break;
             }
         }
     };
    

    首先,初始化HandlerThread并通过handlerThread.getLooper()关联一个在UI线程初始化的mainHandler,这样就可以通过mainHandler在主线程中向HandlerThread中发送消息了,这里要注意一下,mainHandler中的handleMessage()方法是在HandlerThread子线程中执行的,为什么会在子线程中执行呢?因为在初始化mainHandler时传入的是HandlerThread的Looper,而mainHandler是把消息发送到HandlerThread中去,HandlerThread在执行Looper.loop()方法后会循环取出消息并处理消息,所以mainHandler中的handleMessage()方法是在HandlerThread子线程中执行的,那么处理完消息后怎么更新到UI线程呢?

     //在主线程初始化,传入的是主线程中的Looper
     uiHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             //在主线程中处理Message
             switch (msg.what) {
                 case UI_UPDATE:
                     //更新验证码
                     String result = (String) msg.obj;
                     tv_random.setText(result);
                     break;
             }
         }
     };
    

    我们在主线程中初始化了另一个uiHandler 并传入了主线程的Looper,所以此时最后处理消息的回调方法handleMessage()是在主线程中执行的,当HandlerThread处理完逻辑后,通过uiHandler把结果发到主线程中,就可以愉快地来更新UI了,最后别忘了关闭线程:

    @Override
     protected void onDestroy() {
         if (SDK_INT >= 18) {
             handlerThread.quitSafely();
         } else {
             handlerThread.quit();
         }
         super.onDestroy();
     }
    

    HandlerThread API

    返回结果 HandlerThread 备注
    Looper getLooper() 返回和HandlerThread关联的Looper
    int getThreadId() 返回HandlerThread线程的标识符,参见Process.myTid()
    boolean quit() 立即停止Looper的循环,即使messageQueue中有消息也不再处理,在调用此方法后,任何传递Message的方法 (如sendMessage(Message)) 都将失败并返回false
    boolean quitSafely() 当messageQueue中没有Message后,在调用此方法后立即终止Looper,任何传递Message的方法 (如sendMessage(Message)) 都将失败并返回false
    void run() 如果HandlerThread使用单独的Runnable来构造,将执行此方法

    HandlerThread源码分析

    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
    
        //初始化HandlerThread
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
    
       //初始化HandlerThread
       public HandlerThread(String name, int priority) {
            super(name);
            mPriority = priority;
        }
    
        protected void onLooperPrepared() {
        }
    
        @Override
        public void run() {
            mTid = Process.myTid();
            //调用Looper.prepare()初始化Looper 并把Looper放到sThreadLocal中,sThreadLocal可以在不同线程中保存对象副本
            //在Looper的构造方法中就初始化了一个messageQueue,用来存放传入的消息
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            //开始循环从messageQueue中取出消息并处理消息,无消息会阻塞,有消息来时就会唤醒
            Looper.loop();
            mTid = -1;
        }
    
        //返回的即是run方法中产生的Looper
        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            
            // If the thread has been started, wait until the looper has been created.
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    
        //立即停止Looper的循环,即使messageQueue中有消息也不再处理,在调用此方法后,任何传递Message的
        //方法 (如sendMessage(Message)) 都将失败并返回false
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
    
        //当messageQueue中没有Message后,在调用此方法后立即终止Looper,任何传递Message的
        //方法 (如sendMessage(Message)) 都将失败并返回false
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        public int getThreadId() {
            return mTid;
        }
    }
    

    源码很简单,首先HandlerThread继承了Thread,所以HandlerThread本质上还是一个线程,只不过在run()方法中又初始化了Looper和MessageQueue,这样当外界通过Handler向HandlerThread传入消息后,HandlerThread就会取出消息并处理消息,当没有消息时就会阻塞。

    相关文章

      网友评论

        本文标题:Android多线程之HandlerThread

        本文链接:https://www.haomeiwen.com/subject/yjzkkxtx.html