欢迎来到 Android 开发老生常谈的性能优化篇,本文将性能优化划分为内存、网络、布局、卡顿、安装包、启动速度七块,从这七块优化出发,阐述优化的 Application 的方式。
目录
- 内存优化
- 网络优化
- 布局优化
-
- 1、减少布局嵌套
- [2、减少 wrap-content 的使用](#2、减少 wrap-content 的使用)
- [3、使用 include、merge、ViewStub,重复布局复用](#3、使用 include、merge、ViewStub,重复布局复用)
- 卡顿优化
- 安装包优化
-
- 资源优化
-
- 1、按需添加第三方库,避免引入重复库。
- [2、 png转svg,再使用svg转成vector格式。](#2、 png转svg,再使用svg转成vector格式。)
- [3、 删除多余的so库。](#3、 删除多余的so库。)
- [4、 删除多余的 `mipmap` 文件夹。](#4、 删除多余的
mipmap
文件夹。) - [5、 删除未使用到的资源文件。](#5、 删除未使用到的资源文件。)
- 6、动态加载.so文件。
- 7、使用插件化。
- 代码混淆
- 启动速度优化
-
- [App Startup 优化初始化速度](#App Startup 优化初始化速度)
- 懒加载
-
- [ViewStup 按需加载布局](#ViewStup 按需加载布局)
- [Paging 分页](#Paging 分页)
- [ViewPager 2 翻页](#ViewPager 2 翻页)
- 预加载
- 伪加载
- 代码优化
-
- [使用 adb 命令查看启动 Activity 时间](#使用 adb 命令查看启动 Activity 时间)
- [使用 Profile 查看 Activity 方法耗时,对其进行优化](#使用 Profile 查看 Activity 方法耗时,对其进行优化)
- [WorkManager 后台保活](#WorkManager 后台保活)
- 课外小知识
内存优化
在 Android 中,内存优化通常指的是 运行内存优化。运行内存优化 主要关注如何有效地使用和管理应用程序的 RAM (Random Access Memory) 使用,以提高性能,减少延迟,并防止应用程序因为消耗过多内存而被系统杀死。
避免内存泄漏
内存泄漏是指应用程序分配了内存,但未能释放。这会导致应用程序消耗的内存随着时间的推移而增加,可能导致应用程序崩溃或系统性能下降。
使用内存分析工具
Android Studio 提供了一些工具,如 Memory Profiler
,可以帮助开发者分析和理解应用程序的内存使用情况。
优化数据结构和算法
使用更有效的数据结构和算法可以减少内存使用。
数据缓存
合理使用 数据库、网络、图片缓存数据,可以减少不必要的数据、重复数据的存储,减少数据创建和销毁的开销,此外,还可以提高数据的访问速度。
避免频繁的 GC
垃圾回收 (Garbage Collection
简称 GC) ,频繁的垃圾回收会影响应用程序的性能。可以通过减少对象的创建和销毁,以及使用对象池等技术来减少垃圾回收的频率。
网络优化
合并接口请求,减少请求次数
尽可能地减少网络请求次数,可以通过合并请求、使用批量接口等方式实现。
使用网络缓存
对于一些不经常变动的数据,可以使用缓存来减少网络请求。例如,可以使用 HTTP
缓存、数据库缓存或者内存缓存。
使用合适的数据格式
例如,对于大量的数据,可以使用 Protobuf
而不是 JSON
,因为 Protobuf
更加紧凑,可以减少数据的大小,从而减少网络传输的时间。
使用合适的网络库
使用 OkHttp
、Retrofit
等网络库,它们内部已经做了很多优化。
后台同步
对于一些不需要立即返回结果的操作,可以使用后台同步,例如使用 WorkManager
或者 JobScheduler
。
预加载
对于一些用户可能需要的数据,可以提前进行加载。
使用持久连接
使用 HTTP2
,它们支持多路复用,可以减少连接的建立和关闭的开销。
优化图片加载
对于图片,可以使用合适的格式和大小,还可以使用图片加载库,例如 Glide
或者 Picasso
,它们内部已经做了很多优化。
使用前检测网络
在进行网络请求前,先检测网络状态,如果网络不可用,那么可以立即返回错误,而不是等待超时。
布局优化
1、减少布局嵌套
2、减少 wrap-content 的使用
3、使用 include、merge、ViewStub,重复布局复用
卡顿优化
在 Android 系统中,每一秒屏幕刷新速度为 60 帧(FPS),人类眼睛舒适放松时的可视帧率是 24 帧,当某个加载中的界面每秒刷新低于 24 帧时,就会感觉到卡顿。但当屏幕没有绘制需求时,即屏幕的显示的界面为静止时,帧率为 0。
造成卡顿的原因
主线程阻塞
如果在主线程(UI线程)中执行了耗时的操作,如网络请求、数据库操作等,会导致UI更新延迟,从而引发卡顿。
内存泄漏
如果应用中存在内存泄漏,可能会导致内存占用过高,从而引发卡顿。
布局过于复杂
如果布局层级过深或者布局过于复杂,可能会导致布局渲染时间过长,从而引发卡顿。
Bitmap对象过大
如果Bitmap对象过大,可能会导致内存占用过高,从而引发卡顿。
频繁的GC
垃圾回收 (Garbage Collection
简称 GC) 过程需要消耗系统资源,如果频繁触发,可能会导致应用性能下降,出现卡顿现象。
动画效果处理不当
如何避免卡顿问题
避免在主线程中执行耗时操作
可以使用线程、协程、AsyncTask等技术将耗时操作移至后台执行。
优化布局
尽量减少布局的层级,避免过度绘制,使用更高效的 View 或 ViewGroup。
处理内存泄漏
使用
LeakCanary
等工具帮助找到并处理内存泄漏。
优化Bitmap的使用
尽量不要使用过大的
Bitmap
,使用合适的压缩格式和分辨率,及时回收不再使用的Bitmap
。
减少GC的触发
尽量减少内存抖动,避免频繁的对象创建和销毁。 内存抖动 (
Memory Churn
) 是指应用程序在短时间内频繁地创建和销毁对象,这种行为会导致垃圾回收器 (Garbage Collector
) 频繁地运行,从而消耗大量的CPU资源,可能导致应用程序的性能下降,出现卡顿现象。
以上只是一些可能的原因,具体的原因需要通过性能分析工具如 Android Profiler
、BlockCanary
、Systrace
等进行分析。
安装包优化
资源优化
1、按需添加第三方库,避免引入重复库。
2、 png转svg,再使用svg转成vector格式。
3、 删除多余的so库。
删除多余的so库,执行这一操作的前提是:程序所运行的系统 CPU 架构固定是 Android CPU 架构中的某一个。例如:部分第三方提供的 so 库是包含了 armeabi
、armeabi-v71
、arm64-v8a
、x86
等库文件,如果你的程序只运行在 armeabi
CPU 架构上,则可以把其它的 so 库文件删除。
4、 删除多余的 mipmap
文件夹。
一般图标资源使用 mipmap-xhdpi
足够用了,更大的屏幕则使用 mipmap-xxhdpi
、mipmap-xxxhdpi
的分辨率,已经使用 mipmap-xhdpi
或其它更大分辨率的程序,应删除比它小的分辨率文件。
5、 删除未使用到的资源文件。
可通过 Android Studio 菜单栏的 Refactor
→ Remove Unused Resources
功能一键移除未被使用的 drawable
、mipmap
、layout
以及 colors.xml
、strings.xml
文件里面的 color
、string
。
6、动态加载.so文件。
.so
文件可以在用户安装应用到手机后再从服务器上下载到手机的 data
目录下,加载的时候使用绝对路径在 static
关键字里加载。
java
static {
System.loadLibrary("so文件")
}
参考文章
7、使用插件化。
插件化技术支持动态加载代码和动态加载资源,把 Application
的一部分分离出来,对于业务庞大的项目非常有用,极大的分解了 Application
的大小。但又因为插件化需要一定的技术保障和服务端的系统支持,有一定的风险,建议酌情选择。
代码混淆
代码混淆(Obfuscation)是将计算机程序的源代码或机器代码,转换成功能上等价,但是难于阅读和理解的形式的行为。简单来说,就是简化函数名、变量、常量名称,通过减少字符数以达到减小安装包大小的方式。
启用混淆只需要打开 Android 项目,在 Application Model 下中找到 build.gradle
,在该文件添加如下配置即可开启混淆:
groovy
android {
// 打包 release 包时执行
release {
// 启用混淆,默认使用 R8 编译器
minifyEnabled true
// 资源压缩
shrinkResources true
// 定义 自定义混淆规则的文件
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
上面混淆的方式仅适用于代码简单、封装少的项目。项目复杂还请自定义混淆规则,详情请查看往期文章👉【Android】App攻防之代码混淆
启动速度优化
App Startup 优化初始化速度
App Startup
是 Android 官方推出的一个应用程序启动时初始化组件的库。开发人员可以使用 App Startup
来简化启动序列并显式设置初始化顺序。
详情请看👉 App Startup
懒加载
懒加载是一种延迟加载的策略,它指的是在需要时才进行加载和初始化,而不是在应用程序启动或页面加载时就提前加载。这种策略通常用于延迟加载大型资源或组件,以减少启动时间和内存占用。
ViewStup 按需加载布局
详情请看👉 ViewStup
Paging 分页
详情请看👉 Paging
ViewPager 2 翻页
详情请看👉 ViewPager 2
预加载
预加载是在应用程序启动或特定页面加载之前提前加载和准备数据、资源或组件。这有助于提高应用程序的性能和响应速度,因为一些必要的内容已经被提前加载到内存中,而不是等到用户请求时再进行加载。
伪加载
伪加载是一种虚假的加载行为,通常用于模拟加载过程而不实际进行真正的加载。这种策略可以用于创建加载动画或展示加载状态,以提升用户体验,但实际上并没有进行真实的加载操作。
设置启动页
给 app 添加启动页,这应该是最常见的实现伪加载的方式了。当应用启动页播放完成跳转到首页时,你能很明显的感觉到,app 比没有使用启动页之前变得更"流畅"了。
设置启动页参考文章 👉 Android---启动页+闪屏页
其它启动速度优化的方法:1、尽量不要在
Application
的onCreate
中写初始化代码2、减少静态类、静态方法、静态函数的使用,尽量做到用时再初始化
代码优化
使用 adb 命令查看启动 Activity 时间
在 Android 中,我们可以通过 adb
命令:adb shell am start -W packageName/packageName.ActivityName
获取到启动该 Activity
所消耗的时间,如下:
shell
E:\Projects\StepDemo> adb shell am start -W com.binyouwei.demo/com.binyouwei.demo.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.binyouwei.demo/.MainActivity }
Status: ok
Activity: com.binyouwei.demo/.MainActivity
ThisTime: 522
TotalTime: 522
WaitTime: 525
Complete
在上方,我输入了 adb shell am start -W com.binyouwei.demo/com.binyouwei.demo.MainActivity
,系统给我响应了一大串的字符,其中的含义如下:
ThisTime
:指定的 activity 启动耗时。
TotalTime
:应用自身启动耗时 = ThisTime
+ 应用 Application
等资源启动时间
WaitTime
:系统启动应用耗时 = TotalTime
+ 系统资源启动时间
使用 Profile 查看 Activity 方法耗时,对其进行优化
WorkManager 后台保活
使用 WorkManager
实现后台保活,当应用处于后台时,应用所占的内存不会被系统完全回收,再次启动应用相较于第一次启动会更快。
课外小知识
应用启动状态
冷启动
冷启动是指应用程序从头开始启动的过程,这意味着应用程序的进程被终止,用户点击应用图标重新启动应用。在冷启动过程中,应用需要重新创建进程、初始化应用程序和加载 UI 界面。
温启动
温启动是指应用程序在后台保持活动状态并且重新进入前台时的启动方式。在温启动过程中,应用程序的进程仍然存在,但需要重新初始化和加载 UI 界面。
热启动
热启动是指应用程序在后台保持活动状态并且重新进入前台时的快速启动方式。在热启动过程中,应用程序的进程仍然存在,并且可以快速恢复到之前的状态,而不需要重新初始化和加载 UI 界面。