《Android群英传》读书笔记(11)第十章:Android性能优化

来源:互联网 发布:java生成验证码工具类 编辑:程序博客网 时间:2024/06/05 22:49

一、布局优化

1.Android渲染机制

在Android中系统通过VSYNC信号触发对UI的渲染、重绘,其间隔时间是16ms,即1000ms内显示60帧画面的单位时间,如果每次渲染画面的时间保持在16ms之内,那么我们看到的UI就是非常流畅的,如果在16ms内不能完成绘制,那么就会造成丢帧现象,即当前该重绘的帧被未完成的逻辑阻塞,例如一次绘制任务耗时20ms,那么在16ms系统发出VSYNC信号时就会无法绘制,该帧就被丢弃,等待下次信号才开始绘制,导致16*2ms内都显示同一帧画面,这就是画面卡顿的原因。

2.Android中的UI渲染时间工具

Android系统中提供了检测UI渲染时间的工具,打开“开发者选项”选择“Profile GPU Render”(GPU呈现模式分析)并选择“On Screen as bars”(在屏幕上显示为条形图)。这是在屏幕上将显示一些条形图:


图上每一条柱状线都包含三部分,蓝色代表测量绘制Display List的时间,红色代表OpenGL渲染Display List的所需时间,黄色代表CPU等待GPU处理的时间。中间的绿色横线代表VSYNC时间16ms,需要尽量将所有条形图都控制在这条线以下。

3.避免Overdraw

Overdraw(过渡绘制)会浪费很多的CPU、GPU资源,例如系统默认绘制Activity的背景,而如果再给布局绘制了重叠的背景,那么默认Activity的背景就属于无效的过渡绘制,Android系统在开发者选项中提供了这样的一个检测工具——”Enable GPU Overdraw“。激活后可以通过界面上的颜色来判断Overdraw的次数:


通过这个工具可以查看当前区域中的绘制次数,从而尽量优化绘图层次,尽量增大蓝色的区域,减少红色区域。

4.优化布局层级

  • 使用<include>标签重用layout
  • 使用<ViewStub>实现View的延迟加载
ViewStub是一个非常轻量级的组件,它不仅不可视,而且大小为0,下面来演示如何使用<ViewStub>.

首先创建一个布局,这个布局在初始化加载时不需要显示,只在某些情况下才显示出来,下面是一个这样的布局内容:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:text="ViewStub"        android:padding="10dp"        android:textSize="36sp"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></LinearLayout>
然后在布局中通过ViewStub来引用:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.chaoyang805.chapter10performance.MainActivity"    tools:showIn="@layout/activity_main">    <Button        android:onClick="showViewStub"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:paddingBottom="10dp"        android:paddingTop="10dp"        android:text="Show ViewStub"/>    <ViewStub        android:id="@+id/view_stub"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout="@layout/view_stub"/></RelativeLayout>
然后在代码中通过findViewById来找到ViewStub组件,之后就可以用ViewStub.setVisibility(View.VISIBLE);或ViewStub.inflate();将ViewStub显示出来了:

private ViewStub mViewStub;    private TextView tv = null;    View v;    public void showViewStub(View view) {        //一旦ViewStub调用Inflate或setVisiblity后,这个<ViewStub>就不存在了        if (tv == null) {            mViewStub = (ViewStub) findViewById(R.id.view_stub);            v = mViewStub.inflate();        }        tv = (TextView) v.findViewById(R.id.tv);        tv.setText("I'm Visible");//        mViewStub.setVisibility(View.VISIBLE);    }

两个方法的区别在于inflate可以返回引用的布局,从而通过view.findViewById()来找到相应的控件。需要注意的是一旦ViewStub调用了inflate或setVisibility后,这个ViewStub就不存在了,取而代之的是其对应的Layout。

与View.GONE不同的是ViewStub是在显示的时候才会去渲染整个布局,而View.GONE在初始化布局的时候就已经添加在布局树上了,相比之下ViewStub拥有更高的效率。

5.Hierarchy Viewer

Hierarchy Viewer位于SDK/tools下,通过在命令行输入HierarchyViewer.bat来打开,然后选择要测试的进程,点击Load View Hierarchy按钮,显示界面如图:


当点击一个View时会显示这个View的绘制情况,不过第一次点击会显示na,需要再点击菜单中的Profile Node按钮重新计算,就能获取View的绘制信息:


图中的三个小圆点用来表示绘制的效率,绿、黄、红的效率依次降低。

二、内存优化

1.由于Android采用沙箱机制,每个应用所分配的内存大小是有限的,内存太低就会触发LMK——Low Memory Killer机制,通常我们所说的内存包括以下几个部分:

  • 寄存器(Register)
速度最快的存储场所,因为寄存器位于处理器内部,在程序中无法控制
  • 栈(Stack)
存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
  • 堆(Heap)
堆内存用于存放由new创建的对象和数组。在堆中分配的内存由java虚拟机的自动垃圾回收器(GC)来管理
  • 静态存储区(Static Field)
静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态数据变量
  • 常量池(Constant Pool)
JVM虚拟机必须为每个被装载的类维护一个常量池,常量池就是该类型所用到常量的一个有序集合,包括直接常量(基本类型、String)和对其他类型、字段和方法的符号引用。


在这些概念中最容易搞混的就是堆和栈的区分,当定义一个变量,Java虚拟机就会在栈中为该变量分配内存空间,当该变量的作用于结束后这部分内存空间会马上被用作新的空间进行分配,如果使用new的方式创建一个变量,那么就会在堆中为这个对象分配内存空间,即使该对象的作用域结束,这部分内存也不会立即被回收,而是等待系统GC进行回收,堆的大小随着手机的不断发展而不断变大。在程序中可以使用如下代码获得堆的大小,所谓的内存分析,正是分析Heap中的内存状态。

//获取堆内存大小        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);        int heapLargeSize = am.getLargeMemoryClass();
2.获取系统内存信息

获取系统内存信息有两种方式使用dumpsys命令或通过手机中的开发者选项,下面介绍使用dumpsys命令:

  • adb shell dumpsys procstats——可以查看系统内存监视服务
  • adb shell dumpsys meminfo——可以查看正在运行的进程内存信息
3.内存优化举例

Bitmap优化技巧:

  • 使用适当分辨率的和大小的图片
  • 即使回收内存,自Android3.0起,由于Bitmap被放置到了堆内存中,由GC管理,就不需要进行释放了
  • 使用图片缓存,通过内存缓存(LruCache)和硬盘缓存(DiskLruCache)可以更好的使用Bitmap
     代码优化技巧:

  • 对常量使用static修饰符
  • 使用静态方法,静态方法比普通方法提高15%的访问速度
  • 减少不必要的成员变量,如果一个成员可以定义为局部变量就不要声明成成员变量
  • 减少不必要的对象,使用基础类型会比使用对象更加节省资源,同时更应该避免频繁的创建短作用域的变量
  • 尽量不要使用枚举,少用迭代器
  • 对Cursor、Receiver、Sensor、File等对象要非常注意对它们的创建、回收与注册、解注册。
  • 避免使用IOC框架,IOC通常使用注解、反射来进行实现,虽然现在Java对反射的效率已经进行了很好的优化,但大量使用反射依然会带来性能的下降
  • 使用RenderScript、OpenGL来进行非常复杂的绘图操作
  • 使用SurfaceView来替代View进行大量、频繁的绘图操作
  • 尽量使用视图缓存、而不是每次都使用inflate()解析视图。

三、使用TraceView工具优化App性能

1.生成TraceView日志地方法

  • 使用Debug类的方法开启TraceView监听
通过调用Debug.startMethodTracing()方法来开启监听,调用Debug.stopMethodTracing()方法结束监听,可以使用这两个方法来包围要监听的代码块,例如在onCreate()中开启监听,在onDestroy()中结束监听。
  • 通过Android Device Monitor生成TraceView日志
点击DDMS中的start Method Profiling 来开启监听,如图

DDMS提供了两种监听模式:整体监听和抽样监听,整体监听会跟踪每个方法的执行的全部过程,消耗资源大,抽样监听按指定的频率进行抽样调查,需要执行较长的时间来获取较准确的样本数据,执行一段时间后再次点击图中的按钮可以取消监听。
  • 使用AndroidStudio生成TraceView日志
在AndroidStudio中的AndroidMonitor点击startMethodTracing就可以开启监听:

2.分析TraceView日志

TraceView分为两部分:时间轴区域和Profile区域,时间轴区域显示了不同线程在不同的时间段内的执行情况,在时间轴中每一行都代表了一个独立的线程;Profile区域显示了方法的性能分析,主要有以下信息:

  • Incl CPU Time——某方法占用CPU的时间
  • Excl CPU Time——某方法本身(不包括子方法)占用CPU的时间。
  • Incl Real Time——某方法真正执行的时间
  • Excl Real Time——某方法本身真正执行的时间
  • Calls+RecurCalls——调用次数+递归调用次数
每个时间都包含两列,一个是实际时间,另一个是百分比。通常从Incl CPU Time和Calls+RecurCalls开始进行分析,对占用时间长的方法进行重点分析,如果占用时间长且调用次数少那么久可以列为怀疑对象了。
四、使用MAT工具分析App内存状态
1.通过DDMS生成hprof文件
在AndroidStudio中打开DDMS,然后选中要调试的App,点击Dump HPROF File来生成hprof文件,如图:

将文件保存后可以用AndroidStudio打开或者使用MAT工具打开,需要注意的是用MAT打开需要先用platform-tools下的hprof-conv工具进行转换,命令格式为:hprof-conv inputfile outputfile .使用AndroidStudio可以直接打开。
2.通过AndroidStudio生成hprof文件
点击图中的按钮就可以进行调试了

生成的文件如图所示:

打开的文件界面分成了三个部分:
  • class——这部分显示了heap中的所有对象的类
  • instance——显示了heap中对应类的所有实例,点击可以显示对象的属性等信息
  • Reference Tree——显示了对应实例的引用结构
五、使用Dumpsys命令分析系统状态
使用Dumpsys命令可以列出Android系统相关信息和服务状态,Dumpsys命令非常强大,参数配置也非常多,官方信息可以参考以下网址:http://source.android.com/devices/input/dumpsys.html。下面将列出一些常用的参数,通过adb shell dumpsys + 参数 即可。
  • activity——显示所有的activity栈信息
  • meminfo——内存信息
  • battery——电池信息
  • package——包信息
  • wifi——WiFi信息
  • alarm——显示alarm信息
  • procstats——显示内存状态


0 0