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.hqwc.cn/news/206605.html

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

相关文章

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

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

rabbit MQ的延迟队列处理模型示例(基于SpringBoot延时插件实现)

rabbitMQ安装插件rabbitmq-delayed-message-exchange 交换机由此type 表示组件安装成功 生产者发送消息时设置延迟值 消息在交换机滞纳至指定延迟后,进入队列,被消费者消费。 组件注解类: package com.esint.configs;import org.springfra…

网络知识学习(笔记二)

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

Redis 性能管理 主从复制与哨兵模式

目录 redis性能管理 内存碎片率 如何清理内存 面试题 Redis雪崩 Redis集群大面积故障 面试:Redis的缓存击穿 Redis的缓存穿透 Redis的集群高可用方案 redis的主从复制 哨兵模式 redis性能管理 redis的数据缓存在内存当中 info memory #在redis数据库中查…

深入浅出 Linux 中的 ARM IOMMU SMMU I

Linux 系统下的 SMMU 介绍 在计算机系统架构中,与传统的用于 CPU 访问内存的管理的 MMU 类似,IOMMU (Input Output Memory Management Unit) 将来自系统 I/O 设备的 DMA 请求传递到系统互连之前,它会先转换请求的地址,并对系统 I…

软件测试:功能测试常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的,这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求,密码不符合要求(格式上的要求) ④ 密码符合要求&#xf…

双11再创新高!家电行业如何通过矩阵管理,赋能品牌增长?

双11大促已落下帷幕,虽然今年不再战报满天飞,但从公布的数据来看,家电行业整体表现不俗。 根据抖音电商品牌业务发布的收官战报,家电行业创造了成交新纪录,整体同比增长125%。快手官方数据显示,消电家居行业…

在Jupyter Lab中使用多个环境,及魔法命令简介

一、Jupyter Lab使用conda虚拟环境 1、给虚拟环境添加 ipykernel 方法一: 创建环境时直接添加ipykernel 方法:conda create -n 【虚拟环境名称】python3.8 ipykernel实例如下: conda create -n tensorflow_cpu python3.8 ipykernel 方法二&#xff…

Figma最全面的新手指南,从基础到高级,一网打尽

1 Figma界面介绍 Figma基础界面与传统设计软件没有太大区别,有Sketch使用经验的用户几乎可以无缝连接到Figma。 立即体验 免费的在线Figma汉化版即时设计是一款支持在线协作的专业级 UI 设计工具,支持 Sketch、Figma、XD 格式导入,海量优质设…

js进阶笔记之原型,原型链

目录 1、原型对象 constructor 属性 对象原型 2、原型链 3、instanceof 4、原型继承 1、原型对象 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。 面向对象是把事务分解成为…

腾讯云轻量数据库开箱测评,1核1G轻量数据库测试

腾讯云轻量数据库1核1G开箱测评,轻量数据库服务采用腾讯云自研的新一代云原生数据库TDSQL-C,轻量数据库兼100%兼容MySQL数据库,实现超百万级 QPS 的高吞吐,128TB海量分布式智能存储,虽然轻量数据库为单节点架构&#x…

[点云分割] 基于颜色的区域增长分割

效果&#xff1a; 代码&#xff1a; #include <iostream> #include <thread> #include <vector>#include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/search/search.h> #include <pcl/search/kdtree.h> #inclu…