Android线程 - HandlerThread

概述
HandlerThread就是可以处理消息循环的线程,它是一个拥有Looper的线程,可以处理消息循环。简单理解就是一个Thread,内部使用了Looper。
我们都知道,除了在主线程,其他线程必须通过Looper.prepare()创建Looper、Looper.loop()开启消息循环。
举个栗子:

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    private Button mBtnStart;
    private Handler mChildThreadHandler; //子线程的Handler

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Thread thread = new ChildThread();
        thread.start();

        mBtnStart = (Button)findViewById(R.id.btn_start);
        mBtnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mChildThreadHandler.sendEmptyMessage(0);
            }
        });
    }

    class ChildThread extends Thread{
        @Override
        public void run(){
            Looper.prepare(); //给线程创建一个消息循环
            mChildThreadHandler = new Handler(){
                @Override
                public void handleMessage(Message msg){
                    super.handleMessage(msg);
                    mBtnStart.setText("更新按钮文本");
                }
            };
            Looper.loop(); //开始消息循环
        }
    }
}

我的天,在Handler更新UI,出现如下异常。

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

提示只有创建视图(主线程)的线程才能更新UI。mChildThreadHandler是在子线程中开启消息循环,所以没办法切换到主线程中更新UI。

HandlerThread就是在Thread中开启消息循环,但为什么要使用HandlerThread呢?当把Looper转到子线程处理,可以减轻主线程的工作量,使主界面更流畅。

再举一个栗子
用一个Handler更新UI,另一个Handler用于异步耗时任务
布局这里就不贴了,就一个Button一个TextView

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.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private Button mBtnStart;
    private TextView mTvShow;
    private Handler mChildHandler; //子线程的Handler
    private HandlerThread mHandlerThread; //创建异步HandlerThread
    private int flag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvShow = (TextView)findViewById(R.id.tv_show);
        mBtnStart = (Button)findViewById(R.id.btn_start);
        mBtnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mChildHandler.sendEmptyMessage(0);
            }
        });

        mHandlerThread = new HandlerThread("handlerThread");
        mHandlerThread.start();
        mChildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                //这里执行耗时操作后 更新UI
                Message msg = Message.obtain();
                msg.arg1 = ++flag;
                msg.what = 0;
                mUpdateUIHandler.sendMessage(msg);
                return true;
            }
        });
    }

    private Handler mUpdateUIHandler = new Handler(){
        @Override
        public void handleMessage(Message message){
            //这里用于更新UI
            switch (message.what){
                case 0:
                    mTvShow.setText("得到的数据"+message.arg1);
                    break;
            }
        }
    };

@Override
protected void onDestroy() {
    super.onDestroy();
    mHandlerThread.quit();
}

}

效果
这里写图片描述

HandlerThread源码

public class More ...HandlerThread extends Thread {
       int mPriority;
       int mTid = - ;
       Looper mLooper;

       public More ...HandlerThread(String name) { //重点1
           super(name);
           mPriority = Process.THREAD_PRIORITY_DEFAULT;
       }

       public More ...HandlerThread(String name, int priority) {
           super(name);
           mPriority = priority;
       }
       protected void More ...onLooperPrepared() {
       }

       @Override
       public void More ...run() { //重点2
           mTid = Process.myTid();
           Looper.prepare(); //重点3
           synchronized (this) {
               mLooper = Looper.myLooper();
               notifyAll();
           }
           Process.setThreadPriority(mPriority);
           onLooperPrepared();
           Looper.loop(); //重点4
           mTid = - ;
       }
      public Looper More ...getLooper() {
            if (!isAlive()) {
               return null;
           }

           synchronized (this) {
               while (isAlive() && mLooper == null) {
                   try {
                       wait();
                   } catch (InterruptedException e) {
                   }
               }
           }
           return mLooper;
       }
       public boolean More ...quit() { //重点5
           Looper looper = getLooper();
           if (looper != null) {
               looper.quit();
               return true;
           }
           return false;
       }

       public boolean More ...quitSafely() { //重点6
           Looper looper = getLooper();
           if (looper != null) {
               looper.quitSafely();
               return true;
           }
           return false;
       }
       public int More ...getThreadId() {
           return mTid;
       }
   }

HandlerThread是一个Thread,传入的参数就是该线程的名称。
重点3、4通过内部自己创建Looper,开启消息循环,会一直循环,当明确不需要的时候,要记得销毁。
5、6的两个方法分别的作用为:
Looper的quit():将MessageQueue消息池的消息全部清空,无论是延迟消息还是非延迟消息。
Looper的quitSafely():只会清空MessageQueue消息池中所有的延迟消息,那些非延迟的消息会发给Handler去处理。
Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。
再次强调:HandlerThread是串行执行,拥有自己的消息队列,不会阻塞UI线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值