HandlerThread原理分析、实战、最佳实践!

HandlerThread原理分析、实战、最佳实践!本文我们将学习HandlerThread的实现原理,以及开发时,如何正确的使用它来实现我们的开发任务。HandlerThread源码分析设想这样一个场景:我们要在一个线程A中处理业务逻辑,在另一个线程B中,监听A的执行,并进行结果处理。这时我们使用HandlerThread就可以非常简单的实现该功能了。通常我们的线程交互场景,都是UI线程中启动子线程,并且由子线程完成工作任务,最终结果交给UI线程。现在我们的使用场景是,在子线程中监控其他线程的执行结果(这里的其他线程可以是另一个子线程,也可以是UI

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

本文我们将学习HandlerThread的实现原理,以及开发时,如何正确的使用它来实现我们的开发任务。

HandlerThread源码分析


设想这样一个场景:我们要在一个线程A中处理业务逻辑,在另一个线程B中,监听A的执行,并进行结果处理。这时我们使用HandlerThread就可以非常简单的实现该功能了。

通常我们的线程交互场景,都是UI线程中启动子线程,并且由子线程完成工作任务,最终结果交给UI线程。现在我们的使用场景是,在子线程中监控其他线程的执行结果(这里的其他线程可以是另一个子线程,也可以是UI线程),并在子线程中进行结果的处理。

通过描述,我们可以得出2点结论:第一,这个过程中,需要存在2个线程;第二,这两个线程需要进行数据传输(交互)。那么,我们很自然的就想到了Handler机制来实现该功能,但是我们自己在一个子线程中,使用Handler稍显麻烦一些,HandlerThread内置了Handler,简化了我们的操作。

HandlerThread构造函数和继承关系:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {//构造函数1
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {//构造函数2
        super(name);
        mPriority = priority;
    }
}

Jetbrains全家桶1年46,售后保障稳定

逻辑解析:
  1. HandlerThread继承自Thread类,所以它本质上是一个线程类,可以实现线程相关的操作。
  2. 我们来看HandlerThread的构造函数,这里有2个重载版本。
  3. 第一个构造函数接收一个name参数,直接调用了super方法,为该线程命令,且线程优先级为Process.THREAD_PRIORITY_DEFAULT。
  4. 第二个构造函数接收2个参数,可实现线程命名,和设置线程的优先级。

HandlerThread的run方法:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
逻辑解析:
  1. 我们在创建了HandlerThread实例之后,调用start()方法执行,ART虚拟机会帮我们创建一个线程对象,然后在子线程中执行run方法。
  2. run方法中,执行了Looper.prepare()方法,创建了一个Looper对象并绑定到该线程。
  3. 然后在同步块中,执行了mLooper的赋值,调用notifyAll通知Looper已创建完成。
  4. 调用Process.setThreadPriority方法设置线程优先级。
  5. 调用空方法onLooperPrepared,通常用于回调使用。
  6. 最后调用Looper.loop()方法,启动该线程的消息循环。

getLooper方法:

    public Looper getLooper() {
        if (!isAlive()) {//判断线程是否存活
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            //如果线程存活,但mLooper没有创建完成,则等待
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;//返回当前线程的Looper对象
    }
逻辑解析:
  1. getLooper方法可以返回当前线程绑定的Looper对象,我们可以使用该对象作为参数,创建一个该线程的Handler对象,用于线程交互。
  2. getLooper方法首先会判断当前线程是否存活,如果存活,则继续。
  3. 如果线程存活,但Looper对象还没有创建完成,则调用wait方法进行等待(Looper创建完成后,会在HandlerThread的run方法中,调用notifyAll()通知,以继续执行当前逻辑)。
  4. 最后返回Looper对象即可。

HandlerThread的getThreadHandler方法

    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
  1. 如果我们不想自己创建Handler对象,也可以使用HandlerThread为他们提供的Handler对象。
  2. getThreadHandler方法返回一个当前线程的Handler对象。
  3. 它是一个隐藏方法,我们在应用中不可调用。

HandlerThread的退出

    //立即执行退出
    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;
    }

当不需要HandlerThread时,需要调用quit方法或quitSafely方法结束线程管理的Looper消息循环。

HandlerThread实战


HandlerThread的实现其实并不复杂,我们以一个简单Demo来看它的使用。

Demo

public class HandlerThreadTest {
    private final static String TAG = "budaye";
    public static final int HT_MSG = 1;
    private HandlerThread mHandlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
    private Handler mHandler = null;

    public void startHandlerthread(){
        mHandlerThread.start();
        if (mHandler == null){
            mHandler = new Handler(mHandlerThread.getLooper()){
                @Override
                public void handleMessage(@NonNull Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case HT_MSG:
                            Log.d(TAG, "当前线程:" + Thread.currentThread());
                            Log.d(TAG, "收到其他线程发送过来的消息了");
                            break;
                    }
                }
            };
        }
    }
    public void sendMessage() {
        Log.d(TAG, "当前线程:" + Thread.currentThread());
        Message msg = Message.obtain();
        msg.what = HT_MSG;
        mHandler.sendMessage(msg);
    }
}
逻辑解析:
  1. 在HandlerThreadTest里,创建了一个HandlerThread对象。
  2. 调用startHandlerthread方法,开始了HandlerThread对象的线程消息循环,并且创建了一个mHandler对象,用于处理其他线程发送过来的消息。
  3. 我们可以在UI线程中调用sendMessage方法来发送给HandlerThread线程一个消息,并在mHandler对象的handleMessage方法中进行处理。

Demo日志输出

25208-25208/com.example.simpledemo D/budaye: 当前线程:Thread[main,5,main]
25208-25284/com.example.simpledemo D/budaye: 当前线程:Thread[myHandlerThread,5,main]
25208-25284/com.example.simpledemo D/budaye: 收到其他线程发送过来的消息了

我们分别在2个线程中输出了日志。

最佳实践&总结


  1. HandlerThread是一个异步处理的工具类,它可以帮助我们很轻松的实现异步线程处理。
  2. HandlerThread继承自Thread类,它的本质是一个线程类。
  3. HandlerThread实现原理非常简单,它利用了Handler原理,在内部了一个Looper循环,并绑定到当前线程中。
  4. 我们使用创建一个Handler对象,绑定到HandlerTHread对象所对应的Looper,并处理其他线程发送过来的消息。
  5. HandlerThread在构造方法中可以设置线程优先级,默认使用Process.THREAD_PRIORITY_DEFAULT作为默认优先级。
  6. 我们在应用过程中,不要大量使用HandlerThread来执行异步任务,这样会造成线程资源的浪费,大量的异步任务,建议使用线程池来进行操作。
  7. HandlerThread的线程优先级设定一定要注意,如果任务优先级不高,则应该设置成后台任务优先级,避免和UI线程抢占系统资源。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/215481.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • [转载] 三峡大考 二十年一遇洪水惊动长江全流域

    [转载] 三峡大考 二十年一遇洪水惊动长江全流域

  • J2EE是什么意思_main()函数是java程序的执行入口

    J2EE是什么意思_main()函数是java程序的执行入口j2ee   J2EE简介  J2EEJava2平台企业版(Java2Platform,EnterpriseEdition)   J2EE是一套全然不同于传统应用开发的技术架构,包含许多组件,主要可简化且规范应用系统的开发与部署,进而提高可移植性、安全与再用价值。   J2EE核心是一组技术规范与指南,其中所包含的各类组件、服务架构及技术层次,均有共通的标准及规格,让各种依

    2022年10月11日
  • 常用电平转换芯片_硬件电路设计教程

    常用电平转换芯片_硬件电路设计教程在设计数字电路的时候,经常会遇到控制电压不一致,尤其是ARM与一些芯片的电平不一致,比如ARM是5V供电,芯片是3.3V,或者反过来。虽然有的芯片两种电压兼容,不如STM32系列的ARM在3.3V供电的情况的下仍可以兼容5V输入,但是为了安全起见,一般都会使用电平转换芯片。电平转换芯片有两个电源分别为VCCA,对应A1-A8输入;VCCB,对应B1-B8输入.OE使能低…

  • 通达OA 工作流执行出现的异常现象处理(图文)

    通达OA 工作流执行出现的异常现象处理(图文)一个朋友用的工作流,突然说这里面多了很多人不应该看到这个流程的,就是在查询工作时,最上面多了一个“查阅信息”项,然后最下面多了很多人查询的记录。这个项目是从哪里来的呢?普通的流程倒也无所谓了,而有些流程如发文、任职等还是需要保密的,这样让大家查阅肯定是不行的。一开始以为是使用了工作流的转存文件柜的功能,实验了一下,这样转存后并不会在流程里关联查看到查阅信息。又实验了一下转发布公告,同样不是。最后咨

  • java面试题csdn_java底层面试题

    java面试题csdn_java底层面试题问题是:n只奶牛坐在一排,每个奶牛拥有ai个苹果,现在你要在它们之间转移苹果,使得最后所有奶牛拥有的苹果数都相同,每一次,你只能从一只奶牛身上拿走恰好两个苹果到另一个奶牛上,问最少需要移动多少次可以平分苹果,如果方案不存在输出-1输出描述:输出一行表示最少需要移动多少次可以平分苹果,如果方案不存在则输出-1。输入例子:471595输出例子:3im…

  • Java8数组和List相互转换

    Java8数组和List相互转换使用Java1.8中的循环转换数组为List,代码: privateListarrayToList(T[]objects){ returnStream.of(objects).map(object->{ returnobject; }).collect(Collectors.toList()); }

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号