1.布局优化
1.1. Android UI渲染机制
在Android中,系统通过VSYNC信号触发对UI的渲染、重绘,其间隔时间是16ms。这个16ms其实就是1000ms中显示60帧画面的单位时间,即1000/60。如果不能在16ms内完成绘制,那么就会造成丢帧现象。
检测UI渲染时间的工具:开发者选项→GPU呈现模式分析→在屏幕上显示为条形图。
1.2. 避免Overdraw
过度绘制会浪费很多的CPU、GPU资源,检测工具:开发者选项→调试GPU过度绘制→显示过度绘制区域,可以通过界面上的颜色来判断Overdraw的次数
1.3. 优化布局层级
在Android中,系统对View进行测量、布局和绘制时,都是通过对View树的遍历来进行操作的。如果一个View树的高度太高,就会严重影响测量、布局和绘制的速度。
1.4. 避免嵌套过多无用布局
1. 使用<include>
标签重用Layout
<include layout="@layout/common_ui"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
2. 使用<ViewStub>
实现View的延迟加载
<ViewStub>
是一个非常轻量级的组件,它不仅不可视,而且大小为0
<ViewStub
android:id="@+id/not_often_use"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/not_often_use" />
mViewStub = (ViewStub) findViewById(R.id.not_often_use);
View inflateView = mViewStub.inflate();
TextView textView = (TextView) inflateView.findViewById(R.id.tv);
textView.setText("Haha!");
一旦<ViewtStub>
被设置为可见或是被inflate了,<ViewStub>
就不存在了,取而代之的是被infalte的Layout,并将这个Layout的ID重新设置为<ViewStub>
中通过android:inflatedId
属性所指定的ID
<ViewStub>
标签只会在显示时,才去渲染整个布局,而View.GONE
,在初始化布局树的时候就已经添加在布局树上了
1.5. Hierarchy Viewer
工具位于\sdk\tools\hierarchyviewer.bat,选择要调试的进程,然后点击上面的”Load View Hierarchy”
当点击其中一个View的时候,可以显示该View的绘制情况。不过,第一次点击的时候,各种显示的时间都是NA,需要点击菜单中的”Profile Node”按钮重新进行计算,才能获取绘制信息
2. 内存优化
2.1. 什么是内存
由于Android应用的沙箱机制,每个应用所分配的内存大小是有限度的,内存太低就会触发LMK——Low Memory Killer机制。通常情况下内存是指手机的RAM,它包括以下几个部分:
- 寄存器(Registers):速度最快的存储场所,因为位于处理器内部,在程序中无法控制
- 栈(Stack):存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
- 堆(Heap):堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理
- 静态存储区域(Static Field):指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量
- 常量池(Constant Pool):JVM虚拟机必须为每个被装载的类型维护一个常量池,常量池就是该类型所用到常量的一个有序集合,包括直接常量(基本类型,String)和对其他类型、字段和方法的符号引用
当定义一个变量,Java虚拟机就会在栈中为该变量分配内存空间,当该变量作用域结束后,这部分内存空间会马上被用作新的空间进行分配。如果使用new的方式创建一个变量,那么就会在堆中为这个对象分配内存空间,即使该对象的作用域结束,这部分内存也不会立即被回收,而是等待系统GC进行回收。堆的大小随着手机的不断发展而不断变大。在程序中,可以使用如下代码来获得堆的大小
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getLargeMemoryClass();
2.2. 获取Android系统内存信息
1. Process Stats
开发者选项→进程统计信息 或 adb shell dumpsys procstats
2. Meminfo
设置→应用→运行中 或 adb shell dumpsys meminfo
2.3. 内存回收
Java创建了垃圾收集器线程(Garbage Collection Thread)来自动进行资源的管理,这样做的好处是大大降低了程序开发人员对内存管理的繁琐工作。Java的GC是系统自动进行的,但何时进行却是开发者无法控制的,即使调用System.gc()
方法,也只是建议系统进行GC,但系统不一定采纳建议。JVM虚拟机虽然能够自动控制GC,但也难免会存在部分对象忘记回收的现象发生,这就是造成内存泄漏的原因
2.4. 内存优化实例
1. Bitmap优化
Bitmap是造成内存占用过高甚至是OOM(Out of Memory)的最大威胁,下面给出一些使用Bitmap的小技巧:
- 使用适当分辨率和大小的图片
如果图片分辨率与资源文件夹分辨率不匹配或者图片分辨率太高,就会导致系统消耗更多的内存资源。同时,在适当的时候,应该显示合适大小的图片,例如在图片列表界面可以使用图片的缩略图thumbnails,而在显示详细图片的时候再显示原图;或者在对图像要求不高的地方,尽量降低图片的精度 及时回收内存
bitmap.recycle(); // deprecated
- 使用图片缓存
通过内存缓存(LruCache)和硬盘缓存(DiskLruCache)可以更好地使用Bitmap
2. 代码优化
- 对常量使用
static
修饰符 - 使用静态方法,静态方法会比普通方法提高15%左右的访问速度
- 减少不必要的对象,使用基础类型会比使用对象更加节省资源,同时更应该避免频繁创建短作用域的变量
- ……
3. Lint工具
Android Lint工具是Android Studio中集成的一个Android代码提示工具,它可以给你的布局、代码提供非常强大的帮助
4. 使用Android Studio的Memory Monitor工具
Memory Monitor工具是Android Studio自带的一个内存监视工具,使用时需选择所调试的应用。从内存变换的走势图,可以判断关于内存的使用状态,例如当内存持续增高时,可能发生内存泄露,当内存突然减少时,可能发生了GC等
5. 使用TraceView工具优化App性能
5.1. 生成TraceView日志的两种方法
1. 通过代码生成精确范围的TraceView日志
通过调用Debug.startMethodTracing()
方法开启监听,通过调用Debug.stopMethodTracing()
方法结束监听,可以使用这两个方法来包围要监听的代码块,例如在 onCreate()
方法里调用startMethodTracing()
方法来开始监听,在onDestroy()
方法里使用stopMethodTracing()
方法来结束监听。TraceView的日志将会保存到/sdcard/dmtrace.trace文件下,因此需要在Manifest文件中增加如下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2. 通过Android Device Monitor生成TraceView日志
打开Android Studio的Android Device Monitor工具,选择要调试的进程,点击工具栏中的”start method profiling”按钮,选择监听模式(整体监听/抽样监听)
5.2. 打开TraceView日志
Android Device Monitor→File→Open File…
5.3. 分析TraceView日志
1. 时间轴区域
显示不同线程在不同的时间段内的执行情况
2. Profile区域
显示选择的色块所代表的方法在该色块所处的时间段内的性能分析
6. 使用MAT工具分析App内存状态
MAT, Memory Analyzer Tool
6.1. 生成HPROF文件
打开Android Device Monitor工具,选择要监听的线程,并点击菜单栏中的”Update Heap”按钮。在Heap标签中,点击”Cause GC”按钮,就会显示出当前的内存状态
除了手动查看Heap状态,还可以点击菜单栏的”Dump HPROF File”按钮,系统会生成一个(package).hprof文件,不过对这个文件我们还不能直接使用MAT工具进行分析,还需要进行格式转换:
D:\sdk\platform-tools>hprof-conv F:\Heap\com.imooc.heap.hprof heap.hprof
6.2. 分析HPROF文件
打开MAT工具,选择”Open a Heap Dump”选项,等待文件导入后,显示分析结果。
MAT的几个功能:
- Histogram
- Dominator Tree
7. 使用Dumpsys命令分析系统状态
使用Dumpsys命令可以列出Android系统相关的信息和服务状态。使用Dumpsys命令时,只需要输入adb shell dumpsys + 参数
即可,例如使用如下命令来获取Activity栈的详细信息
adb shell dumpsys activity
下面总结了一些常用的Dumpsys参数
activity | 显示所有的Activity栈的信息 |
meminfo | 内存信息 |
battery | 电池信息 |
package | 包信息 |
wifi | 显示Wi-Fi信息 |
alarm | 显示alarm信息 |
procstats | 显示内存状态 |
配合Linux下的shell命令,如grep, find等,可以让Dumpsys命令发挥非常强大的作用