High Performance之android高性能之路

背景:

此乃阅读实践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进度条

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值