Android 性能优化之常用优化点

资源类性能分为:磁盘、CPU和内存,以及与环境密切相关的网络和因为移动网络而显得很重要的电池(耗电)。

1、磁盘

1.1 发现定位工具:Strict Mode 和 Systrace。
对于Strict Mode 的原理,主要是在文件操作(BlockGuardOs.java)、数据库操作(SQLiteConnection.java)和 SharePreferences操作(SharePreferencesImpl.java)的接口中插入检查的代码。

1.2 具体优化点

  • 使用缓存,避免重复多次读写某个文件。(比如多次重复读写 /proc/cpuinfo)
    每次打开、关闭或者读/写文件,操作系统都需要从用户态到内核态的切换,这种切换本身是很耗时的。
  • 延迟写入,对于 SharePreferences 的 commit() ,一个方法中只需保留最后一个。
    每调用一次 commit(),都会对应一次文件的打开和关闭。
  • 在子线程中读写文件
  • 对于读写文件,使用缓存输入输出流
    比如对于 ObjectOutputStream 在序列化磁盘时,在保存对象的时候,每个数据成员会带来一次 I/O 操作,导致 I/O 次数巨多。这时可以使用 BufferedOutputStream 来包装一下。
  • 合理设置 Buffer 的大小
    Buffer 设置太小会导致读写次数太多,Buffer至少应为 4k,Java 默认是 8k。
  • 文件解/压缩效率
    解压磁盘上的文件,建议使用 ZipFile,效率较 ZipInputStream 提升 15% - 27%。
    如果文件不在磁盘上(如网络)或者顺序解压一小部分文件,或者zip文件目录遭到损坏,使用 ZipInputStream。
    ZIP压缩大量小文件时使用 ZipInputStream。
  • 避免重复多次打开数据库,使用缓存数据库连接。(打开数据库比较耗时)
  • 减少使用 AUTOINCREMENT,使用 AUTOINCREMENT 会增加 INSERT 耗时1倍以上。
  • 对于 Bitmap 解码,使用 decodeStream 代替 decodeFile;使用 decodeResourceStream 代替 decodeResource。可以提高效率。

2、内存

2.1 原理

  • 虚拟机的堆内存最大值
    在开发中,要注意App占用的内存大小。Android系统给堆(Heap)设置了一个最大值,可通过 runtime.getruntime().maxmemory() 获取。
  • Low Memory Killer 机制
    当手机剩余内存低于内存的警戒线阈值时,就会触发 LMK 机制杀掉 APP。
  • GC
    没有被GC Root间接或直接引用的对象的内存机会被回收。
    需要注意 GC for Alloc,这种情况是在内存不足以分配给新的对象时触发。它stop the world 的时间因为GC无法并发而变得更长。
    还要注意内存抖动问题。
  • 注意 Activity 泄漏
    Activity 对象会间接或直接引用 View、Bitmap等,所以一旦无法释放,会占用大量内存。
  • 图片缓存
    官方建议使用 Lru 算法来做图片缓存。
    缓存方案: LruCache,DiskLruCache 和 BlobCache。

    内存问题主要包括:
    1、常驻问题(主要是图片缓存)
    2、泄漏问题
    3、OOM
    4、GC问题(GC for alloc,内存抖动)

    后果可能或导致 App Crash、闪退、后台被杀、卡顿,严重就导致 OOM。

2.2 工具

工具 问题 能力
top / procrank 内存占用过大,泄漏 发现
memory info 泄漏 发现 + 初步定为
Strick Mode Activity 泄漏 自动发现 + 定位
Leak Canary Activity 泄漏 自动发现 + 定位
Systrace GC导致的卡顿 发现
Allocation Tracker 申请内存过大;辅助GC log发现的问题;内存抖动 发现 + 定位
MAT 内存泄漏 发现 + 定位

GC log
在 logcat 中可以看到 gc 日志,对于log中 gc 的原因主要有如下几种:

  • GC_EXPLICIT:通过 Runtime.gs() 与 VMRuntime.gc(),SIGUSR1 触发产生的 GC。
  • GC_FOR_ALLOC:没有足够内存空间给予即将分配的内存,这时会触发。这种GC不是并发GC,对卡顿影响更大。
  • GC_FOR_CONCUREENT:当超过堆占用阈值时自动触发,常见、健康GC。
  • GC_BEFORE_OOM:触发 OOM 之前触发 GC。不能并发,不能局部GC,所以耗时长,容易卡顿。
  • GC_HPROF_DUMP_HEAP:在 dump 内存之前触发的 GC。不能并发,不能局部GC,所以耗时长,容易卡顿。

2.3 具体优化点

  • 非静态内部类造成内存泄漏。(内部类持有外部类的引用)
  • Activity 对象被其他对象直接引用导致回收不掉,造成泄漏。
  • Activity 的 Context 被引用造成泄漏。
  • Thread 和 Runnabl 没有执行或没有执行完造成泄漏。
  • 定时器 Timer 没有取消导致的内存泄漏。
  • Handler 和 调用view.postDelayed() 方法后没有移除掉没有执行的Message造成的泄漏。

3、CPU

3.1 工具

  • adb shell top
  • adb shell dumpsys cpuinfo
  • Systrace
  • Traceview

3.2 具体优化点

  • 当App退到后台时,应该停止掉正在执行的不需要的任务。
  • 优化算法。
  • 使用 renderscript 来减少图像处理的 CPU 消耗。

4、电池

4.1 原理
Android 官方建议厂商通过 PowerMonitor 之类的工具来测试每个硬件模块的耗电情况,并配置好 power_profile.xml 文件。power_profile.xml 是一个为了让Android系统能通过硬件调用频率和强度来计算耗电的配置。

从文件的内容可以看出耗电的硬件有:CPU、Wi-Fi、Radio(数据网络)、Senor(感应器)、BlueTooth(蓝牙)、Screen(屏幕)、GPS。还有不属于硬件模块的视频音频

adb查看命令:adb shell dumps power

4.2 具体优化点

  • 对于有些功能,添加黑屏判断。比如有些功能在黑屏状态下是没有意义的情况。
  • 黑屏状态或者在后台时,及时释放动画。
    注意使用 SurfaceView 自定义的动画,SurfaceView 是新开了一个线程用于绘制。
  • 多个 AlarmManager 定时相近任务进行合并。
  • 注意 WakeLock 的及时释放。
    注意间接调用:Mediaserver 是系统服务,是媒体功能的核心,音视频的播放、照相、摄像、录音等功能都需要调用到Mediaserver 实现各种媒体相关的功能。当App调用Mediaserver的某些功能时,例如音视频录制,Mediaserver 会申请WakeLock。比如 AudioRecord 会申请一个WakeLock 来避免手机进入休眠状态影响音频的录制,当不需要时,应调用 release() 释放相应资源。
  • 注意 WakeLock 的计数机制。(不同厂商可能不一样,有修改)
    WakeLock 的 setReferenceCounted 接口用来设置计数机制,true为计数,false为不计数。计数即是每一个 acquire 必须对应一个 release;不计数则是无论有多少个 acquire,一个 release 就可以释放。

参考:腾讯SNG专项测试团队《Android 移动性能实战》

猜你喜欢

转载自blog.csdn.net/iluojie/article/details/80870068