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

news2024/9/20 6:04:24

**在日常开发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.coloradmin.cn/o/1238688.html

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

相关文章

ROSNS3(一)

https://github.com/malintha/rosns3 第一步:clone和构建rosns3客户端 第二步:运行 最详细的ubuntu 安装 docker教程 - 知乎 1. unable to find source space /home/muta/src 解决方法: 将副将将碰到的bug,解决方法_#include &…

论文阅读 Forecasting at Scale (一)

最近在看时间序列的文章,回顾下经典 论文地址 项目地址 Forecasting at Scale 摘要1、介绍2、时间业务序列的特点3、Prophet预测模型3.1、趋势模型3.1.1、非线性饱和增长3.1.2、具有变化点的线性趋势3.1.3、自动转换点选择3.1.4、趋势预测的不确定性 摘要 预测是一…

海外IP代理:数据中心代理IP是什么?好用吗?

数据中心代理是代理IP中最常见的类型,也被称为机房IP。这些代理服务器为用户分配不属于 ISP(互联网服务提供商)而来自第三方云服务提供商的 IP 地址。数据中心代理的最大优势——它们允许在访问网络时完全匿名。 如果你正在寻找海外代理IP&am…

python实现鼠标实时坐标监测

python实现鼠标实时坐标监测 一、说明 使用了以下技术和库: tkinter:用于创建GUI界面。pyperclip:用于复制文本到剪贴板。pynput.mouse:用于监听鼠标事件,包括移动和点击。threading:用于创建多线程&…

基本的弹层,点击弹出

<div class"role"><el-button type"primary" size"mini" click"showDialog true">添加角色</el-button></div><!--控制弹框的显示隐藏visible .sync可以点击X关闭弹框 --> <el-dialog width"…

怎么提高拍摄视频画质和清晰度?这几个方法一定要学会

一、提高拍摄视频画质和清晰度 1、分辨率 分辨率就是我们常说的480P、720P、1080P、4K等等&#xff0c;分辨率越大&#xff0c;在某种程度上视频也就越清晰。虽然原理是这样的&#xff0c;但在不同平台上传视频也会影响最终的画质。 比如超高分辨率的视频不适合在某音。因为…

Electronica上海 Samtec 验证演示 | FireFly™Micro Flyover System™

摘要/前言 在圆满结束的2023慕尼黑上海电子展上&#xff0c;Samtec虎家团队为观众带来了前所未有的丰富体验&#xff1a;产品展示、采访、Demo演示、抽奖互动~ 尤其是Demo演示&#xff0c;虎家工程师FAE Marcus为大家带来了数个精彩的产品与系统讲解演示。其中更不乏合作伙伴…

shell 条件语句

目录 测试 test测试文件的表达式 是否成立 格式 选项 比较整数数值 格式 选项 字符串比较 常用的测试操作符 格式 逻辑测试 格式 且 &#xff08;全真才为真&#xff09; 或 &#xff08;一真即为真&#xff09; 常见条件 双中括号 [[ expression ]] 用法 &…

shopee数据分析软件:了解市场趋势,分析竞争对手,优化运营策略

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

Windows + VS2022超详细点云库(PCL1.8.1)配置

本文在结合多位CSDN大佬的步骤&#xff0c;记录以下最全的点云配置过程&#xff0c;防止走弯路&#xff08;并在最后配上PCL环境配置成功的测试代码-彩色兔子&#xff09; 一、PCL介绍 PCL概述_pcl技术_一杯盐水的博客-CSDN博客 二、准备工作&#xff08;PCL版本的下载&…

Banana Pi BPI-R3 Mini 开源路由器,也能拍出艺术美感

香蕉派BPI-R3 Mini路由器板开发板采用联发科MT7986A(Filogic 830)四核ARM A53芯片设计&#xff0c;板载2G DDR 内存&#xff0c;8G eMMC和128MB SPI NAND存储&#xff0c;是一款非常高性能的开源路由器开发板&#xff0c;支持Wi-Fi6 2.4G/5G&#xff08;MT7976C&#xff09;&am…

网络知识学习(笔记二)

ios模型规定的网络模型一共有7层&#xff0c;但是实际使用过程中&#xff0c;4层的TCP/IP模型是经常使用的&#xff0c;网络知识学习笔记里面也是基于4层TCP/IP模型进行分析的&#xff0c;前面已经讲了&#xff1a;&#xff08;1&#xff09;物理层&#xff0c;&#xff08;2&a…

汇编语言学习笔记

汇编语言的不同种类 as86汇编&#xff1a;能产生16位代码的Intel 8086汇编 mov ax, cs //cs→ax&#xff0c;目标操作数在前GNU as汇编&#xff1a;产生32位代码&#xff0c;使用AT&T系统V语法 movl var&#xff0c; %eax // var→%eax&#xff0c;目标操作数在后内嵌汇编…

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

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

dolphinscheduler有任务一直在运行(问题)目前对数据库解决

dolphinscheduler有任务一直在运行&#xff08;问题&#xff09;目前对数据库解决 危害&#xff1a; 这么多的任务没有结束&#xff0c;会涉及很多问题的&#xff0c;系统的数据盘会不断入职日志&#xff0c;数据量很大&#xff0c; 其实对于dolphinscheduler的性能是下降的&a…

c语言编程(模考3)统计字符串中数字字符的个数

统计字符串中数字字符的个数 #include<stdio.h> int main(){char inputString[100];int digitCount 0;printf("请输入一个字符串&#xff1a;");scanf("%s",inputString);for(int i0;inputString[i]!\0;i){if (inputString[i]>0&&inpu…

怎样实现内网穿透?

第一步&#xff1a;cpolar是一种安全的内网穿透云服务&#xff0c;它将内网下的本地服务器通过安全隧道暴露至公网。使得公网用户可以正常访问内网服务。打开网址 cpolar 下载 。 步骤&#xff1a; 打开网站>点击免费试用>创建账号>下载应用一直点下一步下载完成。第…

@PropertySource适配通配符加载到Environment的一种方案

PropertySource可将配置文件加载到内存&#xff0c;时间有限说干的&#xff0c;PropertySource注解有4个参数&#xff0c;其中value表示要加载文件的路径&#xff0c;这个参数不支持通配符。还有一个参数PropertySourceFactory是加载配置文件的工厂&#xff0c;这两个参数配合使…

【云原生】初识 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 中间件多&#xff0c;学习成本高 2.2.5 服务治理功…