Android客户端性能优化
文中由杰出Android研发工程师(内嵌式小企鹅圈原創精英团队组员)编写,是degao在内嵌式小企鹅圈发布的第一篇原创文章内容,不遗余力地小结共享其在领导干部魅族手机好几个新项目开发设计中的Android手机客户端性能优化工作经验,具有实践活动使用价值!
大家都知道,一个好的商品,除开功能齐全,好的特性也不可或缺。有数据调查报告,近90%的被访者会由于APP特性差而卸载掉,特性也是导致APP客户消沉的一号缘故。
那Android手机客户端特性的指标值都有哪些?怎样发觉和精准定位手机客户端的特性难题?文中融合好几个新项目的开发设计实践活动,得出了要关心的关键指标值新项目,及其精准定位和处理特性难题的一般流程。
性能优化应当围绕于作用开发设计的所有周期时间,而不是做了一次后边便不会再关心。每一次公布版本号前,最好是能对比规范查验下特性是不是合格。
记牢:商品=特性×作用!
一、 特性查验项
1. 运行速率
1)这儿的运行速率指的是冷启的速率,即干掉运用后重启的速率,该项主要是与你的竞争对手比照。
2)不可在Application及其Activity的生命期回调函数中做一切费时间实际操作,实际指标值大约就是你在onCreate,onResume,onStart等回调函数中所花销的总時间最好是不必超出400Ms,不然客户在桌面上点一下你的桌面图标后,将觉得到显著的卡屏。
2. 页面转换
1)运用实际操作时,页面和动漫不需有显著卡屏;
2)可根据在手机上开启 设定->开发者选项->调节GPU过多绘图,随后实际操作运用查询gpu是不是超线开展分析判断;
3. 内存泄露
1)back撤出不可存有内存泄露,简易的查验方法是在退出应用后,用指令`adb shell dumpsys meminfo 应用包名`查询 `Activities Views` 是不是为零;
2)数次进到撤出后的占有运行内存`TOTAL`不可转变 很大;
4. onTrimMemory回调函数
1)运用回应此回调函数释放出来非务必运行内存;
2认证可根据指令`adb shelldumpsys gfxinfo 应用包名-cmd trim 5`后,再)用指令`adb shell dumpsys meminfo 应用包名`查询内存空间
5. 过多绘图
1)打开设置中的GPU过多绘图电源开关,社会各界面过多绘图不可超出2.5x;也就是开启此调节电源开关后,页面总体展现浅色系,尤其繁杂的页面,鲜红色地区都不应当超出全屏的四分之一;
6. lint查验:
1)根据Android Studio中的 Analyze->Inspect Code 对工程项目编码做静态数据扫描仪;找到潜在性的难题编码并改动;
2) 0 error & 0warning,假如的确不可以处理,需得出缘故。
7. 反射面提升:
1)在编码中降低反射面启用;
2)对经常启用的返回值开展Cache;
8. 可靠性:
1)持续两天monkey不可出現卡屏,anr难题。
2)假如运用连接了数据埋点的sdk,例如百度统计sdk,友盟统计sdk等,这种sdk都是会将运用的奔溃信息内容汇报回家,开发人员应每日关心这种统计分析到的奔溃日志,严控运用的奔溃率;
9. 耗电量:
1)运用进到后台管理后不可出现异常耗费用电量;
2)实际操作运用后,退出应用,让运用处在后台管理,一段时间后根据`adb shell dumpsysbatterystats`查询用电量耗费日志看是不是存有出现异常。
二、特性难题普遍缘故
特性难题一般归纳为三类:
1. UI卡屏和可靠性:这类难题客户可立即认知,更为关键;
2. 运行内存难题:运行内存难题具体表现为内存泄露,或是运行内存错误操作造成的运行内存颤动。假如存有内存泄露,运用会持续耗费运行内存,易造成经常gc使系统软件出現卡屏,或是出現OOM出错;运行内存颤动也会造成UI卡屏。
3. 耗电量难题:会危害续航力,主要表现为多余的开机启动,不适当持锁导致没法一切正常休眠状态,系统软件休眠状态后经常唤起系统软件等;
三、UI卡屏普遍缘故和统计分析方法
下边各自详细介绍出現这种难题的普遍缘故及其剖析这种难题的一般流程。
1.卡屏普遍缘故
1)人为因素在UI进程中做轻度用时实际操作,造成UI进程卡屏;
2) 合理布局Layout过度繁杂,没法在16ms内进行3D渲染;
3)同一时间动漫实行的频次太多,造成CPU或GPU负荷太重;
4) View过多绘图,造成一些清晰度在同一帧時间内被绘图数次,进而使CPU或GPU负荷太重;
5) View经常的开启measure、layout,造成measure、layout总计用时太多及全部View经常的再次3D渲染;
6) 运行内存经常开启GC太多(同一帧中经常建立运行内存),造成临时堵塞3D渲染实际操作;
7) 沉余資源及逻辑性等造成载入和实行迟缓;
8)工作中进程优先未设定为Process.THREAD_PRIORITY_BACKGROUND,造成后台管理进程占领UI进程cpu時间片,堵塞3D渲染实际操作;
9) ANR;
2. 卡屏剖析处理的一般流程:
1)处理过多绘图难题
>在设定->开发者选项->调节GPU过多绘图中开启调节,看相匹配页面是不是有过多绘图,如果有先处理掉:
> 精准定位衔接绘图地区
> 运用Android出示的专用工具开展部位确定及其改动(HierarchyView , Tracer for OpenGL ES)
> 精准定位到实际的主视图(xml文件或是View)
> 根据编码和xml文件剖析衔接绘图的缘故
> 融合详细情况开展提升
> 应用Lint专用工具进一步提升
2) 查验是不是有主线任务程干了用时实际操作:
苛刻方式(StrictMode),是Android出示的一种运作时检验体制,用以检验程序执行时的一些不标准的实际操作,最普遍的情景是用以发觉主线任务程的IO实际操作。应用软件能够运用StrictMode尽量的发觉一些编号的疏忽。
> 打开 StrictMode:
>> 针对应用软件来讲,Android 出示了一个最好应用实践活动:尽量早的在
android.app.Application 或 android.app.Activity 的生命期也就能 StrictMode,onCreate()方式就是一个最好的机会,越快打开就能在大量的代码执行途径上发觉违规行为。
>> 监管编码
public voidonCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder()
.detectAll().penaltyLog() .build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll().penaltyLog() .build());
}
super.onCreate();
}
假如主线任务程有互联网或硬盘读写能力等实际操作,在logcat中会出现”D/StrictMode”tag的日志輸出,进而精准定位到用时实际操作的编码。
3)假如主线任务程无用时实际操作,还存有卡屏,有非常大可能是务必在UI进程实际操作的一些逻辑性有什么问题,例如控制measure、layout用时太多等,这时可根据Traceview及其systrace来开展剖析。
4)Traceview:Traceview关键用作热点分析,找到最必须提升的点。
> 开启DDMS随后挑选一个过程,然后点一下上边的“Start Method Profiling”按键(鲜红色小一点变成灰黑色即运行),随后实际操作大家的卡屏UI,随后点一下”Stop Method Profiling”,会开启以下页面:
图上展现了Trace期内各方式启用关联,启用频次及其用时占比。根据剖析能够找到异常的用时涵数并开展提升;
5)systrace:爬取trace:
> 实行以下指令:
$ cd android-sdk/platform-tools/systrace
$ python systrace.py –time=10 -o mynewtrace.htmlsched gfx view wm
> 实际操作APP,随后会转化成一个mynewtrace.html 文档,用Chrome开启:
> 图例以下:
根据剖析上边的图,能够找到显著存有的layout,measure,draw的请求超时难题。
6)导进以下软件,可根据在方式上加上@DebugLog来复印方式的用时:
build.gradle:
buildscript {
dependencies {
//用以便捷调节特性难题的复印软件。给访法再加上@DebugLog,就能輸出该方式的启用主要参数,及其实行時间;
classpath ‘com.jakewharton.hugo:hugo-plugin:1.2.1’
}
}
//用以便捷调节特性难题的复印软件。给访法再加上@DebugLog,就能輸出该方式的启用主要参数,及其实行時间;
apply plugin: ‘com.jakewharton.hugo’
java:
@DebugLog
public void test( int a ){
int b=a*a;
}
四、运行内存特性剖析提升
1.内存泄露
该难题现阶段在新项目中一般用leakcanary基础就能拿下,配备起來也非常简易:
build.gradle:
dependencies {
debugCompile’com.squareup.leakcanary:leakcanary-android:1.3.1′ // or 1.4-beta1
releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.3.1’// or 1.4-beta1
testCompile’com.squareup.leakcanary:leakcanary-android-no-op:1.3.1′ // or 1.4-beta1
}
java:
public class ExampleApplication extends Application {
@Overridepublic void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
一旦有内存泄露,可能在状态栏转化成一条通告,点开可见到泄漏的目标及其引入途径:
2.运行内存颤动
假如编码中存有在onDraw或是for循环系统等数次实行的编码中分配对象的个人行为,会造成运作全过程中gc频次增加,危害ui流畅度。一般这种难题都可以根据lint专用工具检验出去。
五、用电量提升提议
用电量提升主要是留意尽可能不必危害手机上进到休眠状态,也就是恰当申请办理和释放出来WakeLock,此外便是不必经常唤起手机上,关键便是恰当应用Alarm。
六、一些好的编码实践活动
1. 控制地应用Service
2. 当页面不由此可见时增加内存
3. 当运行内存焦虑不安时增加内存
4. 防止在Bitmap上消耗运行内存
对大的图片,先获得照片的尺寸信息内容,依据具体必须展现尺寸测算inSampleSize,最终decode;
public static BitmapdecodeSampledBitmapFromFile(String filename,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to checkdimensions
final BitmapFactory.Options options = newBitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize =
reqHeight);
calculateInSampleSize(options,
reqWidth,
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
public static intcalculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float)reqHeight);
} else {
inSampleSize = Math.round((float) width / (float)reqWidth);
}
}
return inSampleSize;
}
5. 应用提升过的数据信息结合
6. 慎重应用抽象性程序编写
7. 尽量减少应用依赖注入架构
许多依赖注入架构是根据反射面的基本原理,尽管能够让编码看上去简约,可是是妨碍特性的。
8. 慎重应用externallibraries
9. 提升总体特性
10. 应用ProGuard来去除不用的编码
android {
buildTypes {
release{
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile(‘proguard-android.txt’),’src/main/proguard-project.txt’
signingConfig signingConfigs.debug
}
}
11. 谨慎使用出现异常,出现异常对特性不好
抛出异常最先要建立一个新的目标。Throwable 插口的构造方法用名叫
fillInStackTrace() 的当地方式,fillInStackTrace()方式查验栈,搜集启用追踪信
息。只需有出现异常被抛出去,VM 就必需调节启用栈,由于在处理方式中建立了一
个新目标。
出现异常只有用以处理错误,不应该用于操纵操作程序。
下列事例不太好:
try {
startActivity(intentA);
} catch () {
startActivity(intentB);
}
应当用下边的句子分辨:
if (getPackageManager().resolveActivity(intentA, 0) !=null)
不必循环中应用 try/catch 句子,应把其放到最表层,应用 System.arraycopy()替代 for 循环系统拷贝。
大量Android、Linux、内嵌式和物联网技术原創技术性共享敬请期待微信公众平台:内嵌式小企鹅圈。