应用缓慢是用户删除程序的主要原因。启动或滚动列表时几毫秒的延迟会使留存率下降数十个百分点。性能(performance)不仅仅是速度,还包括稳定性:无 ANR、无崩溃、无内存泄漏。本文涵盖性能的各个方面:从内存管理(GC、ARC)到使用工具进行性能分析。更多信息请参阅 官方 Android Performance 指南。
要点
应用性能直接与 jank(用户操作与 UI 响应之间的明显延迟)相关。主要原因:主线程阻塞(UI 线程上的重操作)、频繁的布局重绘(overdraw)、内存泄漏(频繁 GC)、非最优算法(大数据集上的 O(n²))。帧率(FPS)——每秒帧数。为了获得舒适的体验,需要稳定的 60 FPS(Android)或 120 FPS(iPhone Pro、iPad Pro)。VSync 将渲染与屏幕刷新率同步。
当单个帧的渲染超过 16.6 毫秒(对于 60 FPS)或 8.3 毫秒(对于 120 FPS)时,就会发生 jank。GPU 性能分析(Android 上的 Profile GPU Rendering,iOS 上的 Core Animation)显示哪些渲染阶段花费时间最多。主要阶段:Layout(元素布局)、Draw(绘制)、Display(传输到帧缓冲区)。最常见的问题是 XML 中的布局膨胀,尤其是在使用复杂的嵌套 ConstraintLayout 时。
Time-to-Interactive(TTI)——应用完全准备好进行交互所需的时间。TTI 包括冷启动、数据加载和库初始化。Google 建议 TTI 低于 5 秒,Apple 建议主屏幕低于 2 秒。延迟加载——延迟加载内容和库的技术,对改善 TTI 至关重要。在 IT Sectr,我们在所有项目中默认使用延迟初始化。
ANR 和崩溃是移动应用性能的主要敌人。ANR(Application Not Responding)——如果主线程被阻塞超过 5 秒,Android 上会出现的对话框。原因:UI 线程上的同步网络请求、没有协程的数据库操作、没有降采样的大位图解码、主线程死锁。ANR 调用堆栈保存在 /data/anr/traces.txt 中,可以精确定位阻塞位置。
崩溃——应用意外终止。在 Android 上——Exception(Java/Kotlin)或 Signal(原生代码)。在 iOS 上——NSException 或信号(EXC_BAD_ACCESS——访问已释放的内存)。崩溃报告工具:Firebase Crashlytics、Sentry、BugSnag。它们收集堆栈跟踪、设备数据和重现步骤。Stack Overflow——无限递归导致的调用堆栈溢出。OutOfMemoryError——当堆满时。
StrictMode——用于检测线程安全违规的 Android 工具。允许设置规则:ThreadPolicy(禁止在主线程上执行磁盘/网络操作)、VmPolicy(检测 Activity、SQLite、CloseGuard 泄漏)。StrictMode 仅应在调试版本中启用——在发布版本中不应运行。在 iOS 上,等效的工具是 Main Thread Checker(Xcode),它自动检测不在主线程上的 UIKit 调用。
内存泄漏(Memory Leak)——对象在应用不再使用后仍留在内存中的情况。这会直接降低应用性能。在 Android 上,GC(垃圾回收)无法回收存在强引用的对象。典型原因:对 Activity 的静态引用、未取消的回调/观察者、对外部类有隐式引用的内部类、Handler 带有未清除的消息。LeakCanary——用于自动泄漏检测的库。
ARC(自动引用计数)——iOS 中的内存管理模型。每个对象都有一个引用计数器(retain count)。当计数器归零时,内存被释放。当两个对象持有彼此之间的强引用时(A → B 和 B → A),就会发生 Retain Cycle。ARC 永远不会将计数器归零。解决方案:弱引用(weak)或无主引用(unowned)。Weak 在对象释放时自动变为 nil。Unowned 不会变为 nil,但保证对象是存活的。
GC(垃圾回收)在 Android(Java/Kotlin)上运行。GC 定期暂停执行(Stop-the-World 暂停)以查找和释放不可达对象。GC 触发条件:当堆填满到一定百分比时。ARC 在 iOS(Swift/Objective-C)上运行,没有暂停——计数器在每次赋值时原子更新。ARC 更可预测,但在高赋值频率下可能会累积过多的 retain/release 操作。
弱引用(Weak Reference)和强引用(Strong Reference)——引用类型决定 GC/ARC 是否可以释放对象。Strong Reference——只要此引用存在,对象就不会被回收。Weak Reference——GC/ARC 可以回收对象;弱引用变为 nil(在 Swift/Java WeakReference 中)。Unowned Reference(Swift)——释放时不会变为 nil;在对象释放后访问它会导致崩溃。在 Android 上,使用 java.lang.ref.WeakReference 作为弱引用。
使用 LeakCanary 在 Android 上检测泄漏的示例:
// Утечка: анонимный класс держит ссылку на Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// Используем `this@MainActivity`, сохраняя ссылку на Activity
Log.d("TAG", "Handler received message")
}
}
handler.sendEmptyMessageDelayed(0, 60000)
}
}
// Исправление: статический Handler + WeakReference
class SafeHandler(activity: MainActivity) : Handler() {
private val weakActivity =
WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get() ?: return
Log.d("TAG", "Handler received message")
}
}
性能分析是测量应用性能(CPU、内存、网络、功耗)的过程。没有性能分析的盲目优化是无用的——您将不知道代码的哪一部分实际上很慢。
| 工具 | 平台 | 测量内容 | 使用时机 |
|---|---|---|---|
| Instruments (Time Profiler) | iOS | CPU、函数调用、执行时间 | 算法优化、寻找瓶颈 |
| Instruments (Allocations) | iOS | 内存、对象数量、保留计数 | 查找泄漏和过度内存消耗 |
| Instruments (Leaks) | iOS | 循环引用、内存泄漏 | 发布前定期检查 |
| Android Profiler (CPU) | Android | CPU 使用率、线程活动、跟踪 | 查找主线程阻塞 |
| Android Profiler (Memory) | Android | 堆转储、分配跟踪 | 查找泄漏、对象分析 |
| Android Profiler (Network) | Android | 流量、速度、请求时序 | 优化网络调用 |
| LeakCanary | Android | 自动内存泄漏检测 | 在所有开发阶段 |
| StrictMode | Android | 主线程上的磁盘/网络、泄漏 | 调试版本 |
| Traceview / Systrace | Android | 方法跟踪、系统事件 | 深度延迟分析 |
Instruments(Xcode)——用于 iOS 的最强大的工具。Time Profiler 显示哪些函数消耗最多的 CPU。Allocations 跟踪对象的创建和释放。Leaks 自动查找循环引用。性能分析步骤:(1)启动 Instruments;(2)选择模板(用于 CPU 的 Time Profiler);(3)运行有问题的场景;(4)分析调用堆栈——最宽的列是最「热」的函数。
Android Profiler 内置于 Android Studio 中(View → Tool Windows → Profiler)。CPU Profiler 显示每个线程的负载。Memory Profiler——堆转储和分配跟踪。Network Profiler——所有带有时序的 HTTP 请求。Energy Profiler——功耗:WakeLock、Location、Network。对于详细跟踪,使用 Systrace(Android 10+)或 Perfetto——具有微秒精度的系统跟踪。
应用启动是关键性能指标之一。它分为三种类型:冷启动——应用从头开始启动:创建进程、Application.onCreate(Android)/ AppDelegate.applicationDidFinishLaunching(iOS)、加载类、初始化库。温启动——进程存在,但 Activity/ViewController 已被销毁(例如,在屏幕旋转或从内存返回时)。热启动——Activity/ViewController 在内存中,应用仅显示(从其他应用切换)。
冷启动是最重要的指标。在 Android 上包括:(1)启动 Activity——加载 XML、初始化 View;(2)第一帧——到首次渲染的时间。Google 建议:启动 Activity < 200 毫秒,第一帧 < 500 毫秒,TTI < 5 秒。冷启动优化:减少 Application.onCreate(使用协程进行延迟初始化)、使用 SplashScreen API(Android 12+)、推迟库初始化(WorkManager、DI)、删除不必要的 ContentProviders。
在 iOS 上,冷启动包括:加载 Mach-O 二进制文件、dyld(动态链接器)、Objective-C 运行时初始化、应用程序委托、第一个控制器。Chrome Custom Tabs(Android)和 Universal Links(iOS)——用于在没有完全冷启动的情况下在应用中快速打开外部内容的技术。建议在真实的中端设备上测试冷启动。
应用大小是影响安装和更新性能的因素。它影响转化率:每 10 MB 会降低 1% 的转化率。Google Play 建议 APK 大小低于 150 MB;App Store——低于 200 MB(蜂窝网络——100 MB)。主要优化方法:图像压缩(WebP 代替 PNG 节省 25-35%)、矢量化(Android 上的 VectorDrawable,iOS 上的 SF Symbols)、删除未使用的代码(R8/ProGuard)、删除未使用的资源(lint → unused resources)。
App Bundle(Android)——一种发布格式,Google Play 会为每个设备生成优化的 APK。App Bundle 将下载大小减少 20-40%。Dynamic Delivery——按需下载的模块(on-demand feature modules)。在 iOS 上,等效的是 On-Demand Resources(ODR):首次启动后下载的资源(游戏关卡、视频)。
延迟加载——模块和库不在启动时加载,而是根据需要加载的技术。Split APK(Android)和 App Slicing(iOS)——将应用划分为架构槽:arm64-v8a、x86_64。应用大小优化——一个持续的过程:分析 APK 组成(在 Android Studio 中 Analyze APK)、删除重复图标、使用 SVG 代替多种 PNG 密度。在 IT Sectr,我们将构建大小检查纳入每个 MR 的 CI/CD 中。
常见问题
ANR(Application Not Responding)——如果主线程被阻塞超过 5 秒,Android 上会出现的对话框。要避免 ANR,请将所有繁重操作(网络、数据库、文件处理)移至后台线程。在 iOS 上的等效现象是冻结 UI,即应用停止响应触摸。
内存泄漏——当对象因仍有引用而无法释放时。Retain Cycle——在 iOS/Objective-C 中,两个对象相互引用(A → B → A)且 ARC 无法释放任一对象的情况。解决方案:weak/unowned 引用和及时清理回调。
对于 iOS:Instruments(Time Profiler、Allocations、Leaks)。对于 Android:Android Profiler(CPU、Memory、Network)、LeakCanary(内存泄漏)、StrictMode(线程违规)。建议在开发和集成阶段结合使用性能分析。
冷启动——应用从头开始启动:创建进程、加载类、执行 Application.onCreate。温启动——进程存在,但 Activity/ViewController 被重新创建。热启动——Activity/ViewController 已在内存中,仅显示。冷启动最慢(1-5 秒),对用户体验至关重要。
主要方法:删除未使用的资源和代码(使用 R8/ProGuard)、矢量化图像(VectorDrawable、SF Symbols)、压缩 PNG/WebP(Android)、使用 App Bundle 代替 APK、删除不必要的库、对模块使用延迟加载。大小优化可以将 APK 减少 40-60%。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。