HandlerThread 的使用及原理

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

HandlerThread是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例。注意:start()仍然必须被调用。

1.定义

一个Android 已封装好的轻量级异步类

2. 作用

实现多线程
在工作线程中执行任务,如 耗时任务
异步通信、消息传递
实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作

3. 优点

方便实现异步通信,即不需使用 “任务线程(如继承Thread类) + Handler”的复杂组合

4.代码实例

原料:
Button 点击按钮去执行请求随机数的任务
Textview 在界面上更新随机数
HandlerThread 创建Looper 与 Handler 绑定
预期效果 : 点击Button 在界面更新 一次随机数

  public class MainActivity extends AppCompatActivity {
    TextView tv;
    Button btn;   
   //与Ui线程绑定的Handler实例。用来更新界面
    private Handler mUiHandler = new Handler(){
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 2:
                tv.setText((String)msg.obj);
                break;
            default:
                break;
        }
    };
};
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        btn = (Button) findViewById(R.id.btn);
        MyhandlerThread = new HandlerThread("handlerThreadName"); // 创建HandlerThread实例
        //开始运行线程
        MyhandlerThread.start(); 
        Looper loop = MyhandlerThread.getLooper();
        //创建Handler与该线程绑定。
        final   Handler WorkerHandler = new Handler(loop) {
            @Override
            public void handleMessage(Message msg) {
                  Log.v("tag", Thread.currentThread().getName());
                super.handleMessage(msg);
                try {
                    Thread.sleep(1000);  //等于 DownLoadApkFile 
                     //模拟延时处理代表可以执行耗时操作 请求网络数据 ,IO 流
                           Message Mymsg = Message.obtain();
                                   Mymsg .what=2;
                                   Mymsg .obj=msg.obj;
                           mUiHandler.sendMessage(Mymsg);
                           //final String result=(String)msg1.obj; // 第二种方法 直接切换到handler的线程中更新
                            //mUiHandler.post(new Runnable() {
                            // @Override
                            //public void run() {
                            // tv.setText(result);
                                          // }
                                       // });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            
              
            }
        }
        
    @Override
    protected void onDestroy() {
        super.onDestroy();
        MyhandlerThread.quit();
    //  结束线程,即停止线程的消息循环
    }
    }

以下是对于上述代码的解释
MyhandlerThread : 继承自Thread 作用创建实现了 Looper 和 Messagequeue
WorkerHandler : 在工作线程 在里边执行耗时方法 执行完成后 向mUiHandler 发送Message 。
mUiHandler :在主线程中创建的方法 用做更新UI。
执行结果很简单,就是 每点一次button 就会更新 一次随机数

HandlerThread 原理

步骤一 构造方法
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;

     // HandlerThread类有2个构造方法
     // 区别在于:设置当前线程的优先级参数,即可自定义设置 or 使用默认优先级
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
   public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}
}

HandlerThread(String name) 参数为线程名称,线程优先级Process.THREAD_PRIORITY_DEFAULT。
HandlerThread(String name, int priority),name为线程名称,priority为设置的线程优先级。

步骤二 Looper创建
HandlerThread其实还是一个线程,它跟普通线程有什么不同?

protected void onLooperPrepared() {
                }
 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
@Override
public void run() {
    // 1. 获得当前线程的id
    mTid = Process.myTid();
     // 2. 创建1个Looper对象 & MessageQueue对象
    Looper.prepare();
    synchronized (this) { 
       //创建Looper实例
        mLooper = Looper.myLooper();//生成Looper  =myLooper()是通过返回sThreadLocal.get() 获取线程里的looper对象
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();//空方法,在Looper创建完成后调用,可以自己重写逻辑
    Looper.loop();//死循环,不断从MessageQueue中取出消息并且交给Handler处理
    mTid = -1;
}

调用的 Looper.prepare();

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed)); //在threadLocal新建一个looper
}

步骤3:创建工作线程Handler & 复写handleMessage()

  • 源码分析:handlerThread.getLooper()

  • 作用:获得当前HandlerThread线程中的Looper对象

    public Looper getLooper() {
        // 若线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        } 
        // 若当前线程存活,再判断线程的成员变量mLooper是否为null
        // 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
        synchronized (this) {
    
    
        while (isAlive() && mLooper == null) {
            try {
                // 此处会调用wait方法去等待
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    // 上述步骤run()使用 持有锁机制 + notifyAll()  获得Looper对象后
    // 则通知当前线程的wait()结束等待 & 跳出循环
    // 最终getLooper()返回的是在run()中创建的mLooper对象
    return mLooper;   }
    

步骤四 退出方法 (quit 和quitsafely )

   public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

quit和quitSafely都是退出HandlerThread的消息循环。其分别调用Looper的quit和quitSafely方法。
quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息

HandlerThread适合处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。而网络操作相对比较耗时,容易阻塞后面的请求,因此在这个HandlerThread中不适合加入网络操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值