热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

实例分析Android中HandlerThread线程用法

本篇文章主要给大家介绍了AndroidHandlerThread使用介绍以及源码解析,有需要的朋友参考学习下吧。

一、HandlerThread的介绍及使用举例             

HandlerThread是什么鬼?其本质就是一个线程,但是HandlerThread在启动的时候会帮我们准备好一个Looper,并供外界使用,说白了就是使我们在子线程中更方便的使用Handler,比如没有HandlerThread我们要在子线程使用Handler,写法如下:

private Handler mHandler;
    @Override
     public void run() {
        super.run();
         Looper.prepare();
         mHandler = new Handler(){
             @Override
             public void handleMessage(Message msg) {
             }
         };
         Looper.loop();
}

有了HandlerThread就不用我们自己管理Looper了,至于为什么分析源码的时候就明白了。

HandlerThread使用简单介绍:

首先我们要初始化HandlerThread然后调用其start方法,也就是开启线程:

mHandlerThread = new HandlerThread("mHandlerThread");//这里的mHandlerThread其实就是线程的名字
mHandlerThread.start();

接下来初始化一个Handler并且将mHandlerThread中的Looper作为构造函数参数传递给Handler:

mHandler = new Handler(mHandlerThread.getLooper())

这样就保证了Hnadler运行在子线程。并且需要在适合的时机调用HandlerThread的quit方法或quitSafely方法,如Activity销毁的时候:

@Override
protected void onDestroy() {
   //
   super.onDestroy();
   //释放资源
   mHandlerThread.quit();
}

quit()与quitSafely()方法比较(这里只说一些结论,源码可以自己查看):

相同点:

调用之后MessageQueue消息队列均不在接受新的消息加入队列。

不同点:

quit方法把MessageQueue消息池中所有的消息全部清空。quitSafely方法只会清空MessageQueue消息池中所有的延迟消息(延迟消息是指通过sendMessageDelayed或postDelayed等方法发送的消息),非延迟消息则不清除继续派发出去让Handler去处理。

接下来我们完整看一下HandlerThread例子源码:

 public class MainActivity extends Activity {
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private boolean flag;
     //
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
        mHandlerThread = new HandlerThread("mHandlerThread");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper()) {
             @Override
             public void handleMessage(Message msg) {
                 //
                 try {
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 if(flag){
                     Log.i("HandlerThread", "更新:"+new Random().nextInt(1000));
                 }
                 mHandler.sendEmptyMessage(1);
             }
         };
     }
     @Override
     protected void onResume() {
         //
         super.onResume();
         flag = true;
         mHandler.sendEmptyMessage(1);
     }
     @Override
     protected void onPause() {
         //
         super.onPause();
         flag = false;
     }
     @Override
     protected void onDestroy() {
         //
         super.onDestroy();
         //释放资源
         mHandlerThread.quit();
     }
}

运行程序就会在控制台看到每隔两秒有Log打出。至于HandlerThrea的使用就到此为止了,看懂上面小例子就差不多了。

二、HandlerThread的源码分析

HandlerThread源码非常简短,出去注释不到100行,这里就直接全部贴出来了:

 public class HandlerThread extends Thread {
     int mPriority;
     int mTid = -1;
    Looper mLooper;
     public HandlerThread(String name) {
         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) {
        super(name);
        mPriority = priority;
     }
    /**
      * Call back method that can be explicitly overridden if needed to execute some
      * setup before Looper loops.
     */
     protected void onLooperPrepared() {
     }
    @Override
    public void run() {
         mTid = Process.myTid();
         Looper.prepare();
         synchronized (this) {
            mLooper = Looper.myLooper();
             notifyAll();
         }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
         Looper.loop();
         mTid = -1;
     }
     /**
      * This method returns the Looper associated with this thread. If this thread not been started
      * or for any reason is isAlive() returns false, this method will return null. If this thread
      * has been started, this method will block until the looper has been initialized. 
      * @return The 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;
     }
     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;
     }
     /**
      * Returns the identifier of this thread. See Process.myTid().
      */
     public int getThreadId() {
         return mTid;
     }
 }

看第一行就知道了其本质就是一个线程。

6-9行以及17-20行构造函数,也很简单,就是初始化的时候我们可以定义线程名字,还可以传入线程优先级。

初始化完成,紧接着调用start()开发线程就会执行run方法逻辑。

30-41行代码,最重要的就是调用Looper.prepare()以及Looper.loop()方法为我们在子线程准备好一个Looper。并且用变量mLooper记录,调用getLooper()方法的时候返回。

但是,细心的你肯定发现run()方法中有个notifyAll(),getLooper()中有个wait()为什么要加这些鸟玩意?

大家发现没在HandlerThread 例子中Handler的创建是在主线程完成的,创建的时候需要调用HandlerThread的getLooper()获取mLooper作为参数传递给Handler的构造函数,而Looper的创建是在子线程中创建的,这里就有线程同步问题了,比如我们调用getLooper()的时候HandlerThread中run()方法还没执行完,mLooper变量还未赋值,此时就执行了wait()等待逻辑,一直等到run()方法中mLooper被赋值,之后立即执行notifyAll(),然后getLooper()就可以正确返回mLooper了。

明白了吧,不明的话这里需要花些时间好好理解下,好了源码主要部分就分析完了,看到这里相信你对HandlerThread有了一定的了解了。

HandlerThread 还是比较简单理解的,好了,本篇到此为止,希望对你有帮助。


推荐阅读
  • iOS 开发技巧:TabBarController 自定义与本地通知设置
    本文介绍了如何在 iOS 中自定义 TabBarController 的背景颜色和选中项的颜色,以及如何使用本地通知设置应用程序图标上的提醒个数。通过这些技巧,可以提升应用的用户体验。 ... [详细]
  • 本文旨在回顾并总结近期学习的.NET Core基础知识,通过具体的操作指南加深理解,并为初学者提供实用建议,避免常见的错误和陷阱。内容涵盖CentOS的安装配置、.NET Core环境搭建及网站部署等。 ... [详细]
  • 本文探讨了天才与疯子之间的微妙界限,介绍了如何利用巨人的工具提升自我,以及如何通过科学决策、数据洞察和智慧的尺度来指导我们的生活和工作。 ... [详细]
  • 深入理解小程序中的Picker组件
    Picker组件是一种从屏幕底部弹出的滚动选择器,支持多种选择模式,包括普通选择器、多列选择器、时间选择器、日期选择器和省市区选择器。本文将详细介绍Picker的各种属性及其应用场景。 ... [详细]
  • 在许多地理位置选择类的应用程序中,侧边栏是常见的用户界面元素,用于通过选择特定的字母快速定位和选择地点。本文将详细介绍如何在Android应用中创建一个具有波浪效果的自定义侧边栏,以提升用户体验。 ... [详细]
  • 深入解析Spring Boot自动配置机制
    本文旨在深入探讨Spring Boot的自动配置机制,特别是如何利用配置文件进行有效的设置。通过实例分析,如Http编码自动配置,我们将揭示配置项的具体作用及其背后的实现逻辑。 ... [详细]
  • 探讨在使用 Fast-Android-Networking 库时遇到的 addStringBody 方法无法正常工作的问题及其解决方案。 ... [详细]
  • 传送门A-Registration#include#definelllonglongusingnamespacestd;chars[15],t[15]; ... [详细]
  • 本题要求计算给定两个正整数a和b时,2的-a次方与2的-b次方之和,并将结果以最简分数形式表示。输入包括多组测试数据,每组数据包含两个在2到20范围内的整数。 ... [详细]
  • Android Studio 中 Gradle 使用的常见问题与解决方案
    本文探讨了在使用 Android Studio 进行开发时,遇到的与 Gradle 相关的问题,包括每次打开项目都需要下载 Gradle 和 Gradle 插件依赖包下载失败或卡顿等问题,并提供了详细的解决方法。 ... [详细]
  • 在本问题中,游戏的每一步状态均可以视为原始序列的一个子序列。通过定义一个动态规划函数 d(i, j),表示在最优策略下,先手玩家从原序列的第 i 个元素到第 j 个元素之间所能获得的最大分数。 ... [详细]
  • 深入理解动态链接库及其应用
    本文将探讨动态链接库的基本概念,包括Windows下的动态链接库(DLL)和Linux下的共享库(Shared Library),并详细介绍如何在Linux环境中创建和使用这些库。 ... [详细]
  • 请看|间隔时间_Postgresql 主从复制 ... [详细]
  • 本文详细介绍了如何在Python3环境中配置Appium1.4.6,并指导如何连接模拟器进行自动化测试。通过本文,您将了解从环境搭建到模拟器连接的完整流程。 ... [详细]
  • CentOS 7.2 配置防火墙端口开放
    本文介绍如何在 CentOS 7.2 系统上配置防火墙以开放特定的服务端口,包括 FTP 服务的临时与永久开放方法,以及如何验证配置是否生效。 ... [详细]
author-avatar
手机用户2502871761
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有