简介
由于Android技术的成熟,开发出一款Android APP已成为一件非常容易的事情,然而如何开发一款高质量、高性能、用户体验良好的APP却并不是简单的事。无论是我们找工作过程的面试中或是日常开发中,我们都会遇到APP性能优化的问题。如何提升APP性能成为更加关注的话题,因此APP的性能优化变得越来越重要。本文将从以下五个方面浅谈Android的性能优化。
1. 布局优化
布局的优化大致可以从布局的层级和布局的绘制两个角度去思考来优化布局。
1.1 布局层级
Android SDK的tools包中提供了hierarchyviewer工具,可以方便的查看一个页面中布局的关系,方便我们对布局进行分析。同时谷歌也给我们提供了include、merge和ViewStub三兄弟及ConstraintLayout来让我们优化布局。
1.1.1 include、merge和ViewStub三兄弟
- include
include可以提高布局的复用性,大大方便我们的开发,但是它并不能减少我们的布局层级,下面会讲到其中一个兄弟merge, 与它搭配,可以有效的减少布局层级;
- merge
merge中的子view的布局方式取决于父控件是哪个布局,使用merge相当于减少了自身的一层布局,merge一般都是配合include一起使用,既增加了布局的复用性,用减少了一层布局嵌套。
- ViewStub
ViewStub可以理解为按需加载,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。Activity启动在渲染布局时,ViewStub里面的控件是不会发生初始化的,只有ViewStub.inflate()的时候,内部的布局才会初始化,因此一般应用在网络出错,数据为空这样的视图界面中。
1.1.2 ConstraintLayout
面对一些稍微复杂的布局,谷歌为我们提供了一个新的布局ConstraintLayout,ConstraintLayout是Android Studio 2.2中主要的新增功能之一,它是通过约束的方式来控制各个控件的位置和关系。
关于ConstraintLayout的具体用法可以参考Android新特性介绍,ConstraintLayout完全解析这篇文章。
1.2 绘制优化
UI层面上,除了布局上的优化,那基本上就剩绘制渲染方面的优化了。
android提供了一些很好的工具来检测overdraw。Jelly Bean 4.2里,开发者选项菜单里增加了Debug GPU Overdraw的选项。如果你用的是Jelly Bean 4.3 或者 KitKat 设备,在屏幕的左下角会有一个计数展示屏幕overdraw的程度,开启调试会看到界面的哪些部分具有过度的渲染,一般白色:没有overdraw 蓝色:1x overdraw(屏幕绘制了2次) 绿色:2x overdraw 浅红色:3x overdraw 深红色:4x或者更多overdraw。
那么如何进行绘制渲染优化呢?一般从以下面几方面着手。
- 在自定义View的时候,切记尽可能不要在onDraw方法中做耗时操作或创建新的对象等。
- 在对布局设置颜色的时候,当子布局与父布局一样,就不用对子布局设置背景色,当子布局填充父布局的时候,如果给子布局设置了颜色,那么久不用给父布局再次设置背景颜色
2. 内存优化
内存方面的优化需要我们在平时写代码的时候就应当注意如何最少使用内存,如何防止内存泄漏等等,这里并不会包括内存优化的各个方面,仅仅从数据结构、对象复用及内存泄漏三个方面进行浅谈。
2.1 数据结构优化
- 频繁字符串拼接用StringBuilder
- 用ArrayMap、SpareseArray替换HashMap
- 避免内存抖动,例如在循环中实例化大量对象,循环结束这些对象又不需要
2.2 对象复用
- 复用系统自带的资源
- ListView/GridView的ConvertView复用
- 避免在onDraw方法里面执行对象的创建
2.3 避免内存泄漏
- 集合类泄漏
- 单例造成的内存泄漏
- 匿名内部类/非静态内部类造成的内存泄漏
- 资源未关闭造成的内存泄漏(例如读写文件、数据库操作、广播、事件等)
- 合理使用强软弱虚应用来避免内存泄漏
- 检测内存泄漏有一个很好的工具库leakcanary,哪里有泄漏自动给你显示出来;
- 我们也可以使用Memory Monitor进行内存监控。
3. APK大小优化
APK包体积的大小对用户起着举足轻重的作用,安装包过大很容易让用户望而生畏,因此大大减少用户量。那么如何对apk进行瘦身呢?大致可以从以下几方面去考虑。
3.1 使用lint检查工具
在我们日常开发过程中,由于版本的迭代,难免会存在一些没有用到的资源例如布局文件、图片等等,通过lint检查,可以找出项目中没有用到的资源文件,对其进行删除,这样可以减小最终编译打包的apk大小。
当然,我们也可以开启资源压缩,在打包时自动删除无用的资源。
android {
...
buildTypes {
release {
shrinkResources true // 启用删除无效的Resource
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
3.2 考虑用动画或绘制对象来替代图片资源
对于一些图片资源的动画,有些动画可能有多张图片集合而成的帧动画,是否可以考虑使用属性动画完成,关于动画可以参考 Android动画-全面归纳解析
3.3 使用WebP文件格式替换PNG等
可以使用图像的WebP文件格式,而不是使用PNG或JPEG文件。
3.4 插件化
目前市面上提供有插件化框架,可以根据功能模块按需下载,减少安装包大小。
4. APK启动速度优化
应用的启动速度缓慢是我们在开发过程中经常会遇到的问题,比如启动缓慢导致的黑屏,白屏问题。
通常来说,app启动分为冷启动(Cold start)和热启动(Hot start)。
- 冷启动(Cold start)
冷启动是指APP第一次启动,系统还没有为该应用分配新的进程,因此冷启动系统会为APP创建一个新的进程,创建和初始化application,再创建和初始化activity,最后将其显示在界面上。
- 热启动(Hot start)
应用启动后,系统中已有改应用的进程,当我们按back或home键时,应用进程并不会马上销毁而是依然纯在系统中,当我们再次按手机上的应用图标启动应用,这时系统不会再重新创建进程创建和初始化application了,而是直接启动对应的activity,这种方式称为热启动。
了解了两种启动方式的大致过程,那么我们的优化点也就很清晰明了了。
- 如何查看应用的启动时间?
通过adb命令即可查看APP启动时间:
adb shell ps // 查看进程
adb shell am start -W [PackageName]/[PackageName.MainActivity]
例如如下:
D:\AndroidStudioProjects\AndroidQunYinHui>adb shell am start -W com.example.androidqunyinhui/com.example.androidqunyinhui.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.androidqunyinhui/.MainActivity }
Status: ok
Activity: com.example.androidqunyinhui/.MainActivity
ThisTime: 465
TotalTime: 465
Complete
ThisTime和TotalTime一般都是一样,TotalTime表示应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示。
- 如何减少应用的启动时间?
- 尽可能的在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,可考虑线程中完成;
- 对于SharedPreferences的存取,由于sp的特性在初始化时候会对数据全部读出来存在内存中,因此不适合放在主线程中完成;
- 在Activity的生命周期函数中避免做耗时操作。
- 如何防止黑屏或白屏 我们可以为我们的Application设置一种主题,同时为我们的MainActivity设置一个样式,假设样式为一张背景图,这时候当应用启动时,首先显示的是这张背景图。 例如下:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:screenOrientation">portrait</item>
<item name="android:windowBackground">>@mipmap/splash</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
<application
android:name=".App"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//......
</application>
5. APK耗电优化
APK耗电优化在整个性能优化中应该算最不被重视的,这个优先级也确实没有前面几个重要。
在Android4.1版本之后在系统增加了battery info模块,记录一定时间周期内整机及单个App的电量消耗。
Android5.0之后,Google开源的一款用于检测与电池有关的信息和事件的工具--Battery Historian。使用Battery Historian可以可视化分析相关指标如耗电比例、Wifi、蜂窝数据量、WakeLock唤醒次数。
随着Android6.0更新了Battery Historian 2.0加入引起手机状态变化的应用。
具体功能优化点:
1、可以推迟的非面向用户的任务(如定期数据库数据更新);
2、当充电时才希望执行的工作(如备份数据);
3、需要访问网络或 Wi-Fi 连接的任务(如向服务器拉取配置数据);
4、零散任务合并到一个批次去定期运行;
5、当设备空闲时启动某些任务;
6、只有当条件得到满足, 系统才会启动计划中的任务(充电、WIFI…)。