本文由魅族科技有限公司资深Android开发project师degao(嵌入式企鹅圈原创团队成员)撰写,是degao在嵌入式企鹅圈发表的第一篇原创文章,毫无保留地总结分享其在领导魅族多个项目开发中的Androidclient性能优化经验,极具实践价值。

即日起,嵌入式企鹅圈将在之前五个专栏(Linux内核驱动情景分析、资源紧缺型SOC嵌入式架构设计、嵌入式交叉工具链及其应用、嵌入式设计和编程、微信硬件平台和物联网解决方式)新增Android开发专栏!很多其它Android、Linux、嵌入式和物联网原创技术分享敬请关注微信公众号:嵌入式企鹅圈。

众所周知,一个好的产品,除了功能强大,好的性能也不可缺少。有调查显示,近90%的受訪者会由于APP性能差而卸载,性能也是造成APP用户沮丧的头号原因。

那Androidclient性能的指标都有哪些?怎样发现和定位client的性能问题?本文结合多个项目的开发实践,给出了要关注的重要指标项目,以及定位和解决性能问题的一般步骤。

性能优化应该贯穿于功能开发的所有周期。而不是做完一次后面便不再关注。

每次公布版本号前,最好能对照标准检查下性能是否达标。

记住:产品=性能×功能!

一、 性能检查项

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 对project代码做静态扫描;找出潜在的问题代码并改动;

2) 0 error & 0warning,假设确实不能解决。需给出原因。

7. 反射优化:

1)在代码中降低反射调用。

2)对频繁调用的返回值进行Cache;

8.  稳定性:

1)连续48小时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内完毕渲染。

3)同一时间动画运行的次数过多,导致CPU或GPU负载过重;

4) View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重。

5) View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的又一次渲染;

6) 内存频繁触发GC过多(同一帧中频繁创建内存),导致临时堵塞渲染操作;

7) 冗余资源及逻辑等导致载入和运行缓慢;

8)工作线程优先级未设置为Process.THREAD_PRIORITY_BACKGROUND,导致后台线程抢占UI线程cpu时间片,堵塞渲染操作;

9) ANR;

2. 卡顿分析解决的一般步骤:

1)解决过度绘制问题

>在设置->开发人员选项->调试GPU过度绘制中打开调试。看相应界面是否有过度绘制,假设有先解决掉:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

> 定位过渡绘制区域

> 利用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”button(红色小点变为黑色即開始运行)。然后操作我们的卡顿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、嵌入式和物联网原创技术分享敬请关注微信公众号:嵌入式企鹅圈。

[魅族Degao]Androidclient性能优化的更多相关文章

  1. Android客户端性能优化(魅族资深工程师毫无保留奉献)

    本文由魅族科技有限公司资深Android开发工程师degao(嵌入式企鹅圈原创团队成员)撰写,是degao在嵌入式企鹅圈发表的第一篇原创文章,毫无保留地总结分享其在领导魅族多个项目开发中的Androi ...

  2. 01.SQLServer性能优化之----强大的文件组----分盘存储

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 文章内容皆自己的理解,如有不足之处欢迎指正~谢谢 前天有学弟问逆天:“逆天,有没有一种方 ...

  3. 03.SQLServer性能优化之---存储优化系列

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 概  述:http://www.cnblogs.com/dunitian/p/60413 ...

  4. Web性能优化:What? Why? How?

    为什么要提升web性能? Web性能黄金准则:只有10%~20%的最终用户响应时间花在了下载html文档上,其余的80%~90%时间花在了下载页面组件上. web性能对于用户体验有及其重要的影响,根据 ...

  5. Web性能优化:图片优化

    程序员都是懒孩子,想直接看自动优化的点:传送门 我自己的Blog:http://cabbit.me/web-image-optimization/ HTTP Archieve有个统计,图片内容已经占到 ...

  6. C#中那些[举手之劳]的性能优化

    隔了很久没写东西了,主要是最近比较忙,更主要的是最近比较懒...... 其实这篇很早就想写了 工作和生活中经常可以看到一些程序猿,写代码的时候只关注代码的逻辑性,而不考虑运行效率 其实这对大多数程序猿 ...

  7. JavaScript性能优化

    如今主流浏览器都在比拼JavaScript引擎的执行速度,但最终都会达到一个理论极限,即无限接近编译后程序执行速度. 这种情况下决定程序速度的另一个重要因素就是代码本身. 在这里我们会分门别类的介绍J ...

  8. 02.SQLServer性能优化之---牛逼的OSQL----大数据导入

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 上一篇:01.SQLServer性能优化之----强大的文件组----分盘存储 http ...

  9. C++ 应用程序性能优化

    C++ 应用程序性能优化 eryar@163.com 1. Introduction 对于几何造型内核OpenCASCADE,由于会涉及到大量的数值算法,如矩阵相关计算,微积分,Newton迭代法解方 ...

随机推荐

  1. hihoCoder #1783 又一个重复计数

    题目大意 给定一个长度为 $n$ 的字符串 $S$,定义函数 $f(S)$ 表示 $S$ 的不同回文子串的个数.对于 $1\le l \le r \le n$,定义 $S[l,r]$ 为字符串 $S$ ...

  2. 【VBA】利用Range声明Array(一维/二维)

    [说明] B2开始到B?(中间不能有空格),定义一维数组Arr_approver() Dim R_sh As Worksheet Set R_sh = ThisWorkbook.Sheets(&quo ...

  3. nodeJS学习(2)--- NPM 使用介绍

    前言:express 推出了4.X,自己尝试了一下,出现了各种问题.结果查看了各种文档和问题,现在在这个给大家分享下4.X版本的安装. NPM 使用介绍 NPM是随同NodeJS一起安装的包管理工具, ...

  4. cf 512D - Fox And Travelling

    题目大意 给定一颗\(n\le 100\)个点的图,可以进行随机游走,求游走\(k=0...n\)个点的方案数 游走的规则是:每次只能访问一个度数\(\le 1\)的点,并将其删除 分析 看完傻眼 问 ...

  5. SQL索引碎片整理脚本

    原文发布时间为:2011-02-23 -- 来源于本人的百度文章 [由搬家工具导入] reindex是比较好的选择,速度快,但是他不能在线操作INDEXDEFRAG 比较慢,但是可以在线操作rebui ...

  6. sql联合主键,用于多对多,关系映射

    如题.记录下. 复合主键,由多个字段共同确定一行信息 composite key, containing multi cols to fix one element.

  7. Qt5网络请求使用及WebRequest函数

    Qt5模拟curl进行HTTP的head请求, curl -I <url> : #include <QtCore> #include <QNetworkReply> ...

  8. Codeforces 766E Mahmoud and a xor trip(树形DP)

    题目链接 Mahmoud and a xor trip 树形DP.先考虑每个点到他本身的距离和,再算所有点两两距离和. 做的时候考虑二进制拆位即可. #include <bits/stdc++. ...

  9. 使用TensorFlow 来实现一个简单的验证码识别过程

    本文我们来用 TensorFlow 来实现一个深度学习模型,用来实现验证码识别的过程,这里识别的验证码是图形验证码,首先我们会用标注好的数据来训练一个模型,然后再用模型来实现这个验证码的识别. 1.验 ...

  10. 【spring】spring的事务传播性 hibernate/jpa等的事务隔离性

    spring的注解 @Trancational加在controller层,调用了service层的方法,service层的方法也加了@Trancational注解,这时候就出现了事务的嵌套,也就出现了 ...