Android线程优化——整体思路与方法

**在日常开发APP的过程中,难免需要使用第二方库和第三方库来帮助开发者快速实现一些功能,提高开发效率。但是,这些库也可能会给线程带来一定的压力,主要表现在以下几个方面:

  • 线程数量增多:一些库可能会在后台启动一些线程来执行任务,这样会增加系统中线程的数量,从而导致系统资源的浪费。
  • 线程竞争:一些库可能会在同一时间启动多个线程来执行任务,这样会导致线程之间的竞争,从而影响程序的执行效率。
  • 线程阻塞:一些库可能会在执行任务时阻塞主线程,从而导致程序的卡顿和响应速度变慢。

整体思路

为了解决使用第二方库和第三方库代理的线程问题,我选择用下面的思路来进行线程优化:

  • 线程检测,评估优化空间。
  • 线程统计,收集优化范围。
  • 线程和线程池优化,线程数收敛。
  • 线程栈裁剪,减少线程内存。

线程的性能

熟练使用Android上的线程可以帮助你提高应用程序的性能。 本篇文章讨论了使用线程的几个方面:使用UI或主线程; 应用程序生命周期和线程优先级之间的关系; 以及平台提供的帮助管理线程复杂性的方法。 在每一部分,本篇都描述了潜在的陷阱以及如何避免它们的策略。

主线程

当用户启动你的应用程序时,Android会创建一个新的 Linux process 以及一个执行线程。 这个main线程,也称为UI线程,负责屏幕上发生的一切。 了解其工作原理可以帮助你使用主线程设计你的应用程序以获得最佳性能。

内部细节

主线程具有非常简单的设计:它的唯一工作就是从线程安全的工作队列中取出并执行工作块,直到应用程序被终止。 框架从各个地方生成一些这些工作块。 这些地方包括与生命周期信息,用户事件(如输入)或来自其他应用程序和进程的事件相关联的回调。 此外,应用程序还可以在不使用框架的情况下显式地将工作块加入队列。

应用程序执行的任何代码块都会被绑定到一个事件回调上,例如输入,布局填充或绘制。 当某个时间触发一个事件时,事件发生的所在线程会将事件加入到主线程的消息队列。 之后主线程可以处理该事件。

当发生动画或屏幕更新时,系统试图每16ms左右执行一个工作块(负责绘制屏幕),以便以每秒60帧的速度平滑地渲染。 为了让系统达到这个目标,一些操作必须发生在主线程上。 但是,当主线程的消息队列包含太多或太耗时的任务,为了让主线程能够在16ms内完成工作,你应将这些任务移到工作线程中去。 如果主线程不能在16ms内完成执行的代码块,则用户可能感觉到卡顿或UI响应较慢。 如果主线程阻塞大约5秒钟,系统将显示“(ANR)”对话框,允许用户直接关闭应用程序。

从主线程移除多个或耗时的任务,以便它们不会干扰到平滑渲染和对用户输入的快速响应,是你在应用程序中采用线程的最大原因。

线程和UI对象的引用

按照设计,Android UI对象不是线程安全的。 应用程序应该在主线程上创建,使用和销毁UI对象。 如果尝试修改或甚至引用除主线程之外的线程中的UI对象,结果可能是异常,静默失败,崩溃和其他未定义的错误行为。

UI对象引用导致的问题可以划分为两种:显式引用和隐式引用。

显示引用

许多非主线程上的任务在最后都会更新UI对象。 但是,如果某一个线程访问视图层级中的对象,可能会导致应用的不稳定性:如果工作线程修改了同时被任何其他线程引用的对象属性(这里都是指UI对象),则结果是不可预测的。

假设一个应用程序在工作线程上直接引用UI对象。 这个UI对象可能包含对一个View的引用; 但在工作完成之前,该View被从视图层次结构中删除了。 如果该引用将View对象保留在内存中并对其设置属性,用户并不会看到此对象,因为一旦对象的引用消失,应用程序就会删除该对象。

再举另一个例子,View对象(被工作线程引用)持有包含它们的Activity的引用。 如果该Activity被销毁了,但仍有一个工作的线程直接或间接引用它 - 垃圾收集器将不会回收Activity,直到该工作线程执行完成。

在某些Activity生命周期事件(如屏幕旋转)发生时,某些线程工作可能正在运行。 系统将无法执行垃圾回收,直到正在进行的工作完成。 因此,在内存中可能会有两个Activity对象,直到垃圾回收发生。

考虑到以上场景,我们建议你的应用程序的工作线程中不应该包含对UI对象的显式引用。 避免此类引用可帮助你避免这些类型的内存泄漏,同时避免线程竞争。

在所有情况下,应用程序应该只在主线程上更新UI对象。 如果有多个任务希望更新实际的UI,你应该制定一个策略,允许多个线程交互,最终将结果返回到主线程。

隐式引用

在以下代码片段中可以看到带有线程对象代码的常见设计缺陷:

public class MainActivity extends Activity { // …… public class MyAsyncTask extends AsyncTask { @Override protected String doInBackground(Void… params) {…} @Override protected void onPostExecute(String result) {…} }}

这段代码的缺陷是将线程对象MyAsyncTask声明为一些Activity的内部类。 这种声明创建一个对Activity对象隐式引用。 因此,该对象持有对Activity的引用,直到线程工作完成,这样会导致所引用的Activity延迟销毁。 这种延迟会给内存带来更大的压力。

解决该问题的直接解决方案是在自己的文件中定义重载类实例,从而移除对Activity的隐式引用。

另一个解决方案是将AsyncTask声明为静态内部类。 这样做也可以消除隐式引用问题,因为静态内部类与普通内部类不同:普通内部类实例需要外部类的实例才可以实例化,并且可以直接访问其包含的方法和字段。 相比之下,静态内部类不需要引用外部类实例,因此它不包含对外部类成员的引用。

public class MainActivity extends Activity { // …… Static public class MyAsyncTask extends AsyncTask { @Override protected String doInBackground(Void… params) {…} @Override protected void onPostExecute(String result) {…} }}

Android线程优化方案出发点:

  1. 不能通过非UI线程对View进行操作。因为Android的UI不是安全的,如果View能被不同的线程所访问或修改,那么就可能在程序的执行期间,产生不可预期的行为或者并发错误。
  2. 使用线程时,避免在循坏中使用同步,因为获取和释放锁的操作代价很大。会引起CPU资源的损耗。
  3. 处理多线程以及线程间通信时,使用HandlerThread来操作,它内部包装了Looper,记得不用的时候退出/释放资源哦。
  4. 当工作线程与UI线程之间通信的时候,推荐使用AsyncTask(Android 7.0后内部任务变成串行处理,不再会出现以前并行时超过任务数执行饱和策略的情况)
  5. Loader可以用来代替AsyncTask的某些情况,因为Loader的生命周期是独立的(与Application Context有关),当Activity/Fragment销毁重建时,它仍然在,而且它特别使用异步操作,比如AsyncTaskLoader代替AsyncTask也可以实现后者的功能,但是生命周期完全独立于Activity。切记Loader使用完记得销毁。
  6. 当你的Service不需要交互时,请使用可以自动停止的IntentService。
  7. 当你希望延长BroadcastReceiver的生命周期时,例如启动一个后台线程IntentService。在onReceiver中调用BroadcastReceiver.goAsync(),它会返回一个PendingResult对象,这时,广播接收器的生命周期会延长持续到PendingResult.finish()方法调用。
  8. 线程池最好用构造方法手动创建,而不要用Executors来直接调用工厂方法,这样利于明白线程池的运行规则,避免用了错误的线程池导致资源耗尽。
  9. 给线程一个好听的名字,调试时候用。
  10. 线程池设置线程的存活时间,以保证空闲线程准确释放。

有关Android的线程优化就介绍这麽多,更多的Android性能优化问题,可以参考《Android性能优化》这个文档。

优化

1、 定义全局的ThreadMananger管理类,通过一个全局的线程池管理一些new Thread的操作。

2、 定义线程池的时候使用final static结构定义该线程池,以免该类或该方法短时间内重复调用而导致线程增多。

3、 带有定时器的性质的线程(HandlerThread、Timer),退出时一定要做退出或者取消操作(防止内存泄漏),尽可能用HandlerThread替代Timer。

4、 App实现一套符合自身业务(如带有优先级)的线程池。

总结

线程它就像一面双刃剑,用的好的时候可以给我们带来事半功倍等效果,用的不好时就会给我们带来困扰,并且这个困扰还不是一时半会能解决掉的(因为发现问题的时候,往往是到了需要优化期了,各项业务相互牵扯),故在项目初期就需要严格考虑考量这些问题了。**

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/159705.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC 是通向 AGI 的那条路吗?

AIGC 是通向 AGI 的那条路吗? 目录 一、背景知识 1.1、AGI(人工通用智能) 1.1.1、概念定义 1.1.2、通用人工智能特质 1.1.3、通用人工智能需要掌握能力 1.2、AIGC 二、AIGC 是通向 AGI 的那条路吗? 三、当前实现真正的 A…

【云原生】初识 Service Mesh

目录 一、什么是Service Mesh 二、微服务发展历程 2.1 微服务架构演进历史 2.1.1 单体架构 2.1.2 SOA阶段 2.1.3 微服务阶段 2.2 微服务治理中的问题 2.2.1 技术栈庞杂 2.2.2 版本升级碎片化 2.2.3 侵入性强 2.2.4 中间件多,学习成本高 2.2.5 服务治理功…

知虾数据软件:电商人必备知虾数据软件,轻松掌握市场趋势

在当今数字化时代,数据已经成为了企业决策的重要依据。对于电商行业来说,数据更是至关重要。如果你想在电商领域中脱颖而出,那么你需要一款强大的数据分析工具来帮助你更好地了解市场、分析竞争对手、优化运营策略。而知虾数据软件就是这样一…

【React-Router】导航传参

1. searchParams 传参 // /page/Login/index.js import { Link, useNavigate } from react-router-dom const Login () > {const navigate useNavigate()return <div>登录页<button onClick{() > navigate(/article?id91&namejk)}>searchParams 传参…

SpringBoot中使用注解的方式创建队列和交换机

SpringBoot中使用注解的方式创建队列和交换机 前言 最开始蘑菇博客在进行初始化配置的时候&#xff0c;需要手动的创建交换机&#xff0c;创建队列&#xff0c;然后绑定交换机&#xff0c;这个步骤是非常繁琐的&#xff0c;而且一不小心的话&#xff0c;还可能就出了错误&…

phpinfo中的重要信息

phpinfo中的重要信息 1.PHP/操作系统版本信息2.Configuration File(ini配置文件位置)3.Registered PHP Streams(支持的流)4.Registered Stream Filters(支持的流过滤器)5.allow_url_fopen&allow_url_include6.disable_functions7.display_errors8.include_path9.open_based…

【OpenCV实现图像:使用OpenCV进行物体轮廓排序】

文章目录 概要读取图像获取轮廓轮廓排序小结 概要 在图像处理中&#xff0c;经常需要进行与物体轮廓相关的操作&#xff0c;比如计算目标轮廓的周长、面积等。为了获取目标轮廓的信息&#xff0c;通常使用OpenCV的findContours函数。然而&#xff0c;一旦获得轮廓信息后&#…

Java8新特性 ----- Lambda表达式和方法引用/构造器引用详解

前言 在讲一下内容之前,我们需要引入函数式接口的概念 什么是函数式接口呢? 函数式接口&#xff1a;有且仅有一个抽象方法的接口 java中函数式编程的体现就是Lambda表达式,你可以认为函数式接口就是适用于Lambda表达式的接口. 也可以加上注解来在编译层次上限制函数式接口 Fun…

视频云存储EasyCVR平台国标接入获取通道设备未回复是什么原因?该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

【mysql】1153 - Got a packet bigger than ‘max_allowed_packet‘ bytes

执行mysql 语句出现&#xff1a;1153 - Got a packet bigger than max_allowed_packet bytes&#xff1b; 1153-得到一个大于“max_allowed_packet”字节的数据包。 数据包太大了怎么办。该配置吧。 查看max_allowed_packet show global variables like max_allowed_packet;…

Java SPI机制

目录 什么是spi API与SPI区别 SPI实现案例 SPI应用场景 1. JDBC场景 2. ShardingSphere场景 3. Spring 场景 4. SLF4J 日志门面 场景 Java.util.spi下提供了SPI机制&#xff0c;SPI机制&#xff08;Service Provider Interface)其实源自服务提供者框架&#xff08;Serv…

IT变更管理实现服务台高效协同

在当今数字化时代&#xff0c;IT变更管理是IT管理员在服务台中必须面对的重要挑战之一。随着技术的不断发展和市场的快速变化&#xff0c;管理员需要定期进行IT系统和流程的变更&#xff0c;在确保业务稳定性的同时还需提高效率和准确率。 1、全方位的变更计划 IT中应该有一个全…

[开源]Web端的P2P文件传输工具,简单安全高效的P2P文件传输服务

一、开源项目简介 小鹿快传 - 在线P2P文件传输工具 小鹿快传是一款Web端的P2P文件传输工具&#xff0c;使用了WebRTC技术实现P2P连接和文件传输。 二、开源协议 使用MIT开源协议 三、界面展示 产品截图 四、功能概述 简单安全高效的P2P文件传输服务 小鹿快传是一款Web端…

虚拟摇杆OnJoystickMove未被调用,角色不移动

更改interaction type 为 event notification

12、人工智能、机器学习、深度学习的关系

很多年前听一个机器学习的公开课,在Q&A环节,一个同学问了老师一个问题“机器学习和深度学习是什么关系”? 老师先没回答,而是反问了在场的同学,结果问了2-3个,没有人可以回答的很到位,我当时也是初学一脸懵,会场准备的小礼品也没有拿到。 后来老师解释“机器学习和…

回转窑无线测温系统解决方案

方案概述&#xff08;以回转窑监测为例&#xff09; 回转窑通常使用在热电厂、钢铁厂、冶金厂及干燥设备行业。而这些行业一般具有高温、潮湿、粉尘、高压、不便布线或现场无法提供电源等环境特点。同时&#xff0c;回转窑在工作中&#xff0c;具有旋转、高温等特点。为此&…

技术or管理?浅谈测试人员的未来职业发展

我们在工作了一段时间之后&#xff0c;势必会感觉到自己已经积累了一些工作经验了&#xff0c;会开始考虑下一阶段的职业生涯会如何发展。测试人员在职业生涯中的不确定因素还是不少的&#xff0c;由于其入门门槛不高&#xff0c;不用学习太多技术性知识即可入行&#xff0c;所…

RubbleDB: CPU-Efficient Replication with NVMe-oF

RubbleDB: CPU-Efficient Replication with NVMe-oF 前言 这是ATC2023的文章&#xff0c;作者来自哥伦比亚大学这篇工作在LSM-tree多副本存储的场景下&#xff0c;利用NVMe-oF技术避免了LSM-tree副本上的重复合并&#xff0c;减少了CPU开销。 Introduction 为了提供高可用性…

VR全景校园:不被简单定义的校园展示,看的不止“一面”

学校的宣传&#xff0c;还是仅仅依靠一部宣传片来定义的吗&#xff1f;如今&#xff0c;在这个时代&#xff0c;VR全景技术已经越来越成熟了&#xff0c;并逐渐融入了我们的日常生活中&#xff0c;通过VR全景校园&#xff0c;我们可以在网上真实地感受校园的优美环境&#xff0…