您的位置:首页 > 移动开发 > Android开发

High Performance之android高性能之路

2016-04-04 11:29 561 查看

背景:

此乃阅读实践High Performance一书的内容.

硬件性能及电池续航

电池杀手之一:唤醒工具Wakelocks

Wakelocks可以唤醒(保持唤醒状态)移动设备的部分组件。

电池杀手之二:唤醒工具Alarm

Alarm允许开发者设置时间执行特定的操作,特别是App在后台运行或者是设备处于休眠状态的时候。

有时间精确性要求的提醒应该设置一个准确的Alarm(例如,创建一个闹钟App)。其他的情况可以使用不精确的Alarm,操作系统会自动协调合并Alarm来节省电量。下面的代码会每天在alarmTime(操作系统自动协调Alarm的时候,但是不确定是24小时的什么时候)的时候唤醒一次设备:

alarmManager.ssetInexactRepeating(AlarmManager.RTC_WAKEUP,alarmTime,
AlarmManager.INTERVAL_DAY, alarmIntent);


Doze框架

Marshmallow添加了该框架来限制设备频繁的被唤醒。

该框架有这么几个状态:

ACTIVE:Screen is on

INACTIVE:Screen is off, but device is awake

IDLE_PENDING:“Nodding off ” into Doze

IDLE:Device is asleep

IDLE_MAINTENANCE:A short window for all queued alarms and updates to occur

在屏幕关掉后,从INACTIVE到IDLE_PENDING至少需要30分钟,而进入到IDLE还需要30分钟,一旦进入IDLE,设备将会推迟所有的alarms直到maintenance窗口期到来,而每个窗口期IDLE_MAINTENANCE之间的延迟时间会不停的递增,最大为6h.

alarm和wakeLock唤醒操作都被延迟到IDLE_MAINTENANCE执行。

大杀器battery-historian

如何安装

过程有些多,请参考: https://github.com/google/battery-historian

配置

收集跟踪数据之前,最好重置一下数据,为了收集尽可能多的数据,你可以开启报告全部wakelock的功能(这个功能只支持Lollipop及以上的系统):

adb shell dumpsys batterystats --reset
//报告全部wakelock的功能
adb shell dumpsys batterystats --enable full-wake-history
//下载所有从手机上次全充满电之后的数据
adb shell dumpsys batterystats --charged


如果不开启–enable full-wake-history,则会显示



导出日志

adb bugreport > bugreport.txt


启动battery-historian

cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go [--port <default:9999>]


打开浏览器

输入 http://localhost:9999

在显示的网页中载入 刚才的bugreport.txt文件即可。

JobScheduler(作业调度器)

Lollipop(5.0)之前:

每个App中的wakelock和alarm都是相互独立的,所以当众多APP去唤醒的时候,做到同步唤醒是一个几率巧合事件,那么设备可能会被频繁的唤醒。

之后:

JobScheduler可以替代wakelock和alarm运行App的任务。可以看做是“互相协作的wakelock/alarm”API。设置唤醒时间范围,而不是准确的唤醒时间,那么可以这样理解,以前是自己开一辆空车上班,现在是大家拼车上班,这样就节省耗油。

//kJobId allows me to run multiple JobScheduler runs at the same time <snip>
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
String delay = mDelayEditText.getText().toString(); //read delay time(s) from UI
if (delay != null && !TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString(); //Read deadline time from UI
if (deadline != null && !TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked()); //checkbox to force JS only when idle
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked()); //checkbox to force JS only when charging
mTestService.scheduleJob(builder.build());


JobScheduler的另一个很炫酷的特点就是执行重复工作,并且可以指定执行的周期是线性还是指数衰减。如果你的App不处在前台,你可能不需要持续这种频繁的更新,因此,你可以让它们降低更新频率。当App重新打开的时候,用户仍然可以看到最新的数据,后台数据使用量却减少了。

JobScheduler有两种延时工作的衰减方式:线性(慢衰减)或指数(快衰减)

UI渲染性能

研究显示,0-100ms的延迟会让用户感知到瞬时的卡顿,延迟100-300ms则会让用户感觉到迟缓,300-1000ms的延迟让用户感觉“手机卡死了”,1000ms以上的延迟会让用户想去干别的事情了。

什么是卡顿(Jank)

内容的快速加载很重要,渲染的流畅性也很重要。Android团队把滞缓,不流畅的动画定义为jank,一般是由于丢帧引起的。大部分Android设备一秒刷新屏幕60次(也有例外,比如早期的Android设备的刷新频率是50fps甚至更低)。由于屏幕每16ms刷新一次(1s/60fps = 16ms/f),所以保证每帧的渲染时间小于16ms是非常重要的。如果有一帧跳过了,用户将会感知到动画的跳跃,这样的体验是非常不好的。为了保证动画的流畅度,我们将研究如何使得整个屏幕在16ms内渲染完成。在这一章,我们将分析一些常见的问题,看看如何保证你的UI不出现卡顿。

重要工具——层次结构查看器(Hierarchy Viewer)

为什么没有显示绘制时间



需要点击最右边的按钮。

三色点的意思是啥?



分别代表着 该view 在纵列的view层中measure,layout,draw 的相对速度。绿色表示其为速度快的50%,黄色表示为速度慢的50%,红色则表示其为该层最慢的view. 很显然,红色就是我们需要优化的目标。

优化注意点

一个视图的子视图越多,渲染就会越费时,减少视图树形结构的深度,App每一帧的渲染就会变快。

RelativeLayout就需要经常对它的子View测量两次来确保所有子View被放置在了正确的位置。LinearLayout如果有子View设置了layout weight属性,也需要测量两次来确定子View的确切尺寸。如果是嵌套的LinearLayout或者是RelativeLayout,测量的次数将会呈指数增长(两层嵌套将会进行4次测量,3层嵌套会进行8次测量等等)

全部的测量、布局和绘制的时间最好在16ms以下,这样才能保证屏幕运行的流畅性。

从Tree overview中可以看到,屏幕上有很多个视图,渲染树的结构相对扁平。渲染树越扁平越好,因为视图XML文件的深度越深,渲染所需的时间就越长。然而,图中的结构虽然是扁平的,可还是需要26ms来进行绘制,说明扁平的结构也有可能会卡顿,也需要去考虑如何进行优化(正常情况16ms才算流畅)

消减资源

减少每个View所使用的资源也能减少时间消耗

减少资源消耗方法之一:减少状态图片

重复绘制

检测工具之一:Debug GPU Overdraw

开发者选项菜单里增加了Debug GPU Overdraw的选项。如果你用的是Jelly Bean 4.3 或者 KitKat 设备,在屏幕的左下角会有一个计数展示屏幕overdraw的程度。我认为这个工具对快速检测overdraw问题还是十分有效的。

* 白色:

没有overdraw

* 蓝色:

1次 overdraw(屏幕绘制了2次)

* 绿色:

2次 overdraw(屏幕绘制了3次)

* 浅红色:

3次 overdraw

* 深红色:

4次或者更多次overdraw

检测工具之二:Hierarchy Viewer中的overdraw

此时要关闭Debug GPU Overdraw ,开着Debug GPU Overdraw Hierarchy Viewer的内容就没有了

Capture layers 另存为PSD , 免费的psd预览器 GIMP

然后可以用图层的形式来具体的看到重复绘制的情况。打开它真的需要耐心啊….



通过遍历图层,能大概了解图层重绘的情况,实际来看这种重绘的场景还是很少的。

KitKat(4.4)的Overdraw Avoidance

在KitKat或者更新版本的设备里,overdraw的影响被大幅度的削减了。这项技术被称为Overdraw Avoidance,系统可以自动地移除简单的overdraw(比如一个视图被其它视图完全覆盖住了)

分析卡顿的宏观工具Profile GPU Rendering

Android在Jelly Bean及更新的系统里加入了一个Profile GPU Rendering

16ms的诅咒



绿色横线 是 16ms的诅咒,超过代表有卡顿。

绿色竖线 是当前画面绘制所到位置

每条竖线代表每次屏幕绘制draw(蓝色),prepare(紫色),process(红色),execute(黄色)

通过Logcat来看丢帧



内核级运行状态检测工具 Systrace



WASD键可以缩放(W,S)和左右滑动(A,D)。

VSYNC,由一些间隔均匀的宽条组成。VSYNC是告诉操作系统刷新屏幕的信号。每一条之间都间隔16ms(也就是中间的白色间隔)。当VSYNC事件触发的时候(在每个色条的尾部),surface flinger(红色高亮方框包含几种颜色的长条)会从view buffer(没展示出来)里选一个View,然后绘制到屏幕上。理想情况下,surfaceflinger会每隔16ms触发一次(没有jank的情况下),因此如果出现长条空缺则表明surfaceflinger丢掉了一次VSYNC更新——屏幕没有在规定的时间更新(这就导致jank的原因)。你可以看到在这段轨迹2/3的位置有这样一个间隔(在绿色方框中)。

* VSYNC-sf 提示surface flinger有16ms的时间来渲染屏幕。里面棕色的条状表示16ms的长度。

* surfaceflinger从队列里抓取一个View(注意黄色方框里的buffer中View数量从2变为1)。完成之后,View被发送给GPU,这样,屏幕绘制就完成了。

* VSYNC-app告诉app去渲染新的View(并且发送一个16ms的定时器)

* 当VSYNC一开始,droid.yahoo.att就不停的重复这个过程,测量View的布局,然后发送给RenderThread……循环往复。

从下图可以看出来,com.sankuai.meituan 产生了帧缓存,surfaceFlinger通过acquireBuffer获取到帧缓存,



接下来surfaceFlinger会将缓存数据交给GPU,然后将缓存队列中的帧缓存给release掉。并且com.sankuai.meituan所对应的绿色方块也没有了。



5.1的改进

每一帧都用一个标有F的圆点来表示。正常渲染的帧用绿色的圆点来表示,渲染慢(还有很慢的)的帧用黄色或者红色圆点来表示。



选择圆点然后按下m就可以凸显出一帧的数据更易于分析。按下m键后,高亮显示该帧的区间来,连耗用的时间都标识出来。



警告提醒



总结:

systrace 还是很强大的,其从内核底层的角度反应整个系统的一系列运作,待后面更多的对底层知识的理解贯通后,还需多参考一下该工具。

短时间的等待(1S内)不要使用Process进度条

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: