本文首发地址 https://h89.cn/archives/313.html
最新更新地址 https://gitee.com/chenjim/chenjimblog

一、安卓应用设置线程优先级的可行性及意义

在安卓应用开发中,是可以设置线程优先级的。设置线程优先级具有重要意义,它能够优化应用在多线程环境下的性能,避免一些不必要的线程占用过多的CPU资源,从而影响到关键线程的执行,特别是在处理并发任务时。比如在同时进行网络请求、本地文件读取以及UI更新等任务的情况下,合理设置线程优先级可以让UI线程保持响应性,避免出现卡顿现象,提升用户体验。

安卓系统的多线程架构

安卓系统基于Linux内核开发,线程的内部实现也是基于Linux的轻量级进程。在安卓应用中,多个线程可以并发执行,每个线程都扮演着不同的角色。从大的方向区分,有UI线程(主线程)和后台线程(background线程)。UI线程主要负责处理用户界面的更新和交互,例如响应用户的触摸操作、绘制屏幕等。如果在UI线程执行耗时操作,就会导致界面冻结,这是非常糟糕的用户体验。因此,通常将一些耗时的操作,如网络请求、文件读写等,放到后台线程执行。这种多线程结构的存在就引出了线程优先级设置的需求,通过合理设置线程优先级,能够更好地协调不同线程之间的资源分配。

线程优先级在安卓应用中的必要性

联想一个实际场景,当一个音乐播放类的安卓应用既在后台进行音乐文件的加载(涉及到磁盘I/O操作),同时又要响应用户随时可能的暂停、播放等交互操作(UI操作)时,就需要确保UI操作对应的线程有较高的优先级,而音乐文件加载这种相对耗时且不那么紧急的操作对应的线程优先级可以相对较低。这样即使在大量音乐文件加载的过程中,用户与界面交互时也不会感觉到明显的延迟。

二、安卓应用设置线程优先级的方法

使用Thread类实例的setPriority方法

  • 方法来源及原理
    这种方式来源于Java,是一种基于Java的Thread属性来设置线程优先级的方法。Java为线程优先级指定了范围,通过Thread类中的静态常量来表示。
    这些静态常量分别为:MIN_PRIORITY,其值为1,表示线程可拥有的最低优先级;NORM_PRIORITY,值为5,是默认分配给线程的优先级;MAX_PRIORITY,值为10,表示线程可拥有的最高优先级。例如新创建一个Thread对象时,如果不设置优先级,默认就为NORM_PRIORITY。
  • 方法的使用及限制
    示例:
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
      }
    }); 
    thread.setPriority(Thread.MIN_PRIORITY);  
    thread.start();  

    这个方法修改线程优先级时,参数范围必须在MIN_PRIORITY和MAX_PRIORITY范围之间,如果超过这个范围,将抛出错误。而且设置成功时,此线程的优先级将设置为指定的参数和该线程的线程组的最大允许优先级中的较小者。也就是说,如果某个线程组对最大优先级有一个小于MAX_PRIORITY的限制,即便传参为MAX_PRIORITY,最终实际设置的优先级可能也会小于这个值。例如,如果线程组最大允许优先级为8,设置MAX_PRIORITY(10)时,实际会设置为8。

    使用Process类的setThreadPriority方法

  • 方法的优势及原理
    这是Android推荐的方式。Android系统基于Linux内核,与Linux中的进程管理类似,如何更好地与Android系统的资源管理机制相结合是优先级设置需要考虑的。Process类提供的setThreadPriority方法在这方面更加适配Android系统。
    它的参数是定义在Process类中的整数常量,这些常量可以指定更细致的线程优先级级别。
  • 常见常量及含义
    THREAD_PRIORITY_DEFAULT:值为0,表示应用程序线程的标准优先级。
    THREAD_PRIORITY_LOWEST:值为19,这是最低可用优先级,仅针对那些真的不想在发生任何其他事情的情况下运行的任务。
    THREAD_PRIORITY_BACKGROUND:值为10,是标准后台优先级,优先级略低于正常优先级,它对用户界面的影响非常小。
    THREAD_PRIORITY_FOREGROUND:值为 -2,这是用户正在进行交互的UI线程优先级,但此优先级应用程序不能自己设置(在代码中不能设置),当用户在界面之间进行切换时,系统将自动调整应用线程的优先级。
    THREAD_PRIORITY_DISPLAY:值为 -4,系统显示线程的优先级,该优先级应用也不能自己设置。
    THREAD_PRIORITY_URGENT_DISPLAY:值为 -8,最重要的显示线程的优先级,用于合成屏幕和检索输入事件,应用程序不能更改为此优先级。
    THREAD_PRIORITY_VIDEO:值为 -10,视频线程的标准优先级。
    THREAD_PRIORITY_AUDIO:值为 -16,音频线程的标准优先级。
    THREAD_PRIORITY_URGENT_AUDIO:值为 -19,最重要的音频线程的标准优先级。
  • 使用方法示例(假设在线程执行时设置优先级):
    // 设置线程优先级为后台优先级
    android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);  

    三、安卓应用线程优先级设置的相关案例

    案例一:文件下载应用

  • 场景描述
    在一个文件下载应用中,通常会有多个任务并发进行。例如,同时下载多个文件,并且还可能在下载过程中进行用户界面的更新,如显示下载进度条、文件大小等内容。如果这些任务的线程优先级没有合理设置,就可能会导致问题。
  • 线程优先级设置方案及效果
    将文件下载的线程设置为相对较低的优先级(如使用Process.THREAD_PRIORITY_BACKGROUND),因为文件下载相对于界面的更新不那么紧急。而将负责UI更新的线程设置为相对较高的优先级(虽然不能直接设置为THREAD_PRIORITY_FOREGROUND,但可以根据实际情况设置一个比文件下载线程更高的优先级)。这样,即使在进行大规模文件下载时,UI仍然能够保持流畅的更新,用户能够及时看到下载进度等信息,提升了用户体验.

    案例二:图像编辑应用

  • 场景描述
    图像编辑应用在处理图像时可能会执行如滤镜应用、图像缩放、旋转等操作,这些操作可能比较耗时。同时,用户也可能在编辑过程中进行界面操作,如切换滤镜、调整参数等。
  • 线程优先级设置方案及效果
    对于耗时的图像编辑操作线程,可以设置较低的优先级。将响应用户交互操作(如点击滤镜)的线程设置较高的优先级。这样能够保证在进行复杂图像编辑计算时,用户界面仍然能够快速响应用户的交互请求,避免出现长时间的等待或者卡顿情况。例如,当用户快速切换滤镜时,即使图像正在进行复杂的算法处理(如高分辨率图像的模糊滤镜处理),应用也能够迅速响应滤镜切换的操作。

    四、安卓应用线程优先级设置的注意事项

    两种设置方法的选取原则

  • 不推荐Java原生API(Thread类)的原因
    在Android系统中,虽然可以用Thread类实例的setPriority方法来设置线程优先级,但不建议这样做。因为Android提供的Process类的setThreadPriority方法,其API划分的级别更多,更适合在Android系统中进行设定细致的优先级。例如,Process类可以针对音频线程(THREAD_PRIORITY_AUDIO、THREAD_PRIORITY_URGENT_AUDIO)、显示线程(THREAD_PRIORITY_DISPLAY、THREAD_PRIORITY_URGENT_DISPLAY)等特殊类型的线程进行精准的优先级设定,这是Thread类无法做到的。
  • 安卓推荐的Process类的优势体现场景
    当处理与系统紧密相关的线程任务时,如音频播放线程或者显示相关的更新线程,Process类提供的定义好的常量能够更好地按照系统期望的调度方式进行设置。例如在开发一个视频播放器应用,设置视频播放线程的优先级为THREAD_PRIORITY_VIDEO能够确保在多任务场景下系统按照合理的优先级分配资源,保证视频播放的流畅性,同时不会过度影响其他任务的性能。

    线程优先级常量的正确使用

  • Java原生API与AndroidAPI的常量区别
    在Java原生Thread类中,线程优先级的值越大,优先级越高,有MIN_PRIORITY = 1、NORM_PRIORITY = 5、MAX_PRIORITY = 10这些常量。而在Android API中,定义在Process类中的常量情况则不同,值越小,优先级越高。例如THREAD_PRIORITY_LOWEST = 19是很低的优先级,而THREAD_PRIORITY_URGENT_AUDIO = - 19是很高的优先级。所以一定不要混用这两者的常量,否则会导致线程优先级设置错误,影响整个应用的性能甚至稳定性。
  • 如何避免常量混用导致的问题
    在代码开发过程中,建立良好的代码注释习惯。例如,在设置线程优先级的地方,注释说明使用的是哪种API的常量,如“//这里使用Android API的Process类的常量设置音频线程优先级为紧急音频优先级THREAD_PRIORITY_URGENT_AUDIO”。同时,对开发团队进行相关知识的培训也是很有必要的,确保所有开发人员清楚两种API的区别。并且可以在代码的构建或者审查过程中,添加一些规则来检查是否存在常量混用的情况。

    对UI线程的特殊处理

  • UI线程优先级的系统自动调整机制
    UI线程(THREAD_PRIORITY_FOREGROUND,值为 -2)的优先级由系统自动调整。这意味着在代码中不能随意设置UI线程的优先级。当用户在界面之间进行切换等操作时,系统会根据当前系统资源的使用情况和用户交互的频繁程度,自动调整UI线程的优先级。例如在应用从后台切换到前台时,系统会提高UI线程的优先级,以确保界面能够快速响应用户的操作。
  • 后台线程对UI线程的影响及预防措施
    如果后台线程优先级设置不合理,可能会抢占过多资源,影响UI线程的响应速度。为了避免这种情况,对于后台线程,尤其是那些执行耗时操作(如大规模数据处理或者长时间等待的网络请求)的线程,应该设置较低的优先级,如THREAD_PRIORITY_BACKGROUND。通过这种方式,将更多的CPU资源留给UI线程,确保流畅的用户体验。例如在一个社交应用中,当上传照片的同时,用户进行界面切换操作,如果上传照片的后台线程优先级过高,可能会导致切换界面时出现短暂卡顿,所以要合理设置上传照片线程的优先级。

    五、常见安卓应用线程优先级设置的错误与解决办法

    错误一:超出范围设置线程优先级值

  • 问题表现
    在使用Thread类实例的setPriority方法时,容易出现设置的优先级值超出MIN_PRIORITY(1)到MAX_PRIORITY(10)范围的情况。例如设置一个错误的值为15时,就会导致程序抛出异常。这种异常如果不处理,会导致应用程序崩溃,影响应用的稳定性。
  • 解决办法
    在设置Thread类的优先级时,要增加边界值检查。可以使用条件语句进行判断,如if (priority >= Thread.MIN_PRIORITY && priority <= Thread.MAX_PRIORITY) {,在满足条件的情况下再执行setPriority方法。并且在开发过程中,通过单元测试来覆盖这种边界值的情况,确保不会出现超出范围的值被设置为优先级的情况。

    错误二:混淆Java原生API和AndroidAPI常量

  • 问题表现
    由于Java原生API和Android API的线程优先级常量使用规则不同,在设置线程优先级时可能会出现混淆。例如将Java原生的线程优先级取值概念套用到Android API中,本应使用小值表示高优先级(AndroidAPI特点),却错误地设置了大值,从而导致线程优先级设置错误。这可能会使关键线程得不到足够的资源,影响应用的性能,如UI线程响应迟缓或者后台线程过度占用资源。
  • 解决办法
    一方面,要加强开发人员对两种API常量区别的学习和理解。在团队内部可以开展专项的知识分享会或者培训课程。另一方面,可以在代码中通过使用明确的命名规范或者封装函数来减少错误。例如可以创建一个函数专门用于设置基于Android API的线程优先级,函数内部只接受符合Android API常量定义的参数,这样在调用这个函数时就不容易出错。同时在代码审核过程中,重点审查涉及线程优先级设置的代码部分,确保没有常量混淆的情况。

    错误三:错误设置UI线程优先级

  • 问题表现
    虽然系统会自动调整UI线程的优先级,但是有些开发者可能会错误地尝试在代码中设置UI线程的优先级。这可能会干扰系统正常的调度机制,导致无法预测的问题,比如界面卡顿或者无法响应用户操作。
  • 解决办法
    牢记UI线程优先级由系统自动控制这一原则。如果发现有代码试图设置UI线程优先级,要及时调整代码逻辑,改为将重点放在合理设置其他与UI线程存在资源竞争关系的后台线程优先级上,以确保UI线程能够正常工作,例如通过降低后台线程的优先级来保障UI线程的资源需求。

本文链接:安卓应用中线程优先级的设置 - https://h89.cn/archives/313.html

版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。

标签: 线程, 优先级

添加新评论