faywong 发布的文章

在android的开发中,我们不时需要客观的测量和评估android应用程序的帧率。这篇文章我打算分享下这几天我在这方面的尝试:

1) fps meter

这个软件是我使用最早的,但是对手机的要求很高。第一是root,root完毕也不是每个手机上都能很好的工作。现在小米的MIUI对于root这件事是越来越不大方了,所以也更加添加了使用的难度。

root成本高昂,root完毕仍然不一定适用于所有的手机。所以只能忽略之。

2) dumpsys

dumpsys是android系统提供的用于dump系统服务的状态信息的命令行工具。

给它提供gfxinfo参数时,它能在Logcat输出中给出UI相关的性能数据。但是M以前版本的android自带的dumpsys工具只能给出120帧的时序信息。且需要手动合并时间值。

例如systemui模块的UI方面的信息如下:

```bash
** Graphics info for pid 1269 [com.android.systemui] **

Recent DisplayList operations

DrawRenderNode

DrawRenderNode

DrawRenderNode

DrawRenderNode

DrawRenderNode

DrawRenderNode

DrawRenderNode

RestoreToCount

multiDraw

DrawText

DrawRect

DrawBitmap

DrawColor

DrawLayer

multiDraw

DrawText

multiDraw

DrawText

multiDraw

DrawText

DrawBitmap

DrawBitmap

DrawRect

DrawRect

multiDraw

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

DrawShadow

DrawRoundRect

DrawBitmap

DrawBitmap

DrawBitmap

multiDraw

DrawText

multiDraw

DrawText

DrawShadow

DrawRoundRect

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

DrawBitmap

multiDraw

DrawText

Caches:

Current memory usage / total memory usage (bytes):

TextureCache 20874472 / 75497472

LayerCache 9633792 / 50331648 (numLayers = 4)

Garbage layers 0

Active layers 0

RenderBufferCache 2019328 / 8388608

GradientCache 0 / 1048576

PathCache 631960 / 33554432

TessellationCache 219536 / 1048576

TextDropShadowCache 6289840 / 6291456

PatchCache 14272 / 131072

AssetAtlas 15745024 bytes ( 15.02 MB )

FontRenderer 0 A8 5242880 / 5242880

FontRenderer 0 RGBA 4194304 / 4194304

FontRenderer 0 total 9437184 / 9437184

Other:

FboCache 9 / 16

Total memory usage:

49120384 bytes, 46.84 MB

Profile data in ms:

StatusBar/android.view.ViewRootImpl@647809c (visibility=0)

Draw Prepare Process Execute

0.61 0.19 6.45 1.72

0.59 0.25 6.14 1.19

0.89 0.23 5.95 1.32

0.78 0.28 7.68 1.48

3.48 0.22 5.72 1.43

0.61 0.17 5.90 1.18

0.61 0.19 5.48 1.17

0.67 0.19 5.58 1.17

0.50 0.19 5.58 1.19

0.60 0.21 5.06 1.13

0.79 0.27 8.64 1.77

0.81 0.25 6.46 1.46

0.81 0.19 5.51 1.19

0.95 0.30 8.34 2.36

0.79 0.27 6.59 1.76

1.25 0.23 6.41 1.20

2.13 0.31 2.12 1.25

0.64 0.22 5.66 1.29

0.61 0.21 5.56 1.21

0.68 0.16 5.58 1.24

0.83 0.27 6.95 1.54

0.61 0.19 5.77 1.22

0.67 0.22 5.49 1.17

1.12 0.24 8.62 1.74

21.00 7.25 35.60 23.48

Heads Up/android.view.ViewRootImpl@17c714d0 (visibility=8)

Draw Prepare Process Execute

View hierarchy:

StatusBar/android.view.ViewRootImpl@647809c

691 views, 1095.36 kB of display lists

Heads Up/android.view.ViewRootImpl@17c714d0

63 views, 93.02 kB of display lists

Total ViewRootImpl: 2

Total Views: 754

Total DisplayList: 1188.38 kB
```

这些数据采样的时间太短,且需要进一步的加总才能可视化。所以不是很方便。

Google似乎也意识到了这一点,在M Preview中他们支持了累计的帧率数据:

```bash
Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706
Number High input latency: 142
Number Slow UI thread: 17270
Number Slow bitmap uploads: 1542
Number Slow draw: 23342
```

但是适用面太窄了,只能在android M中使用。

3) GameBench

这个工具目前来看用起来很方面,且直接给出了可视化结果,非常人性化且可靠。上个截图:

![GameBench](/uploads/2015-08-31-01.png)

使用步骤:
1. 下载[gamebench-3.2.2p.apk](/uploads/gamebench-3.2.2p.apk)并安装到 Android 手机上
2. 下载[GameBench-Installer_v4.0.jar](/uploads/GameBench-Installer_v4.0.jar)并通过
```bash
java -jar GameBench-Installer_v4.0.jar
```
在PC上运行设置过程(注意:需要使用jdk8,不然jar包会报错),直到出现以下成功界面:
![GameBench PC 设置](/uploads/2015-08-31-02.png)

3. 在 android 上运行 GameBench app,并在其中运行待评估的目标应用,运行一段时间后得到如下结果:

![GameBench 结果](/uploads/2015-08-31-03.png)

比如创建一个用户时只有在不存在的时候才需要插入,查了好久,终于搞明白:

```sql
INSERT INTO users(id, alias, email) SELECT 00001, 'faywong', 'wangfei584521@163.com' from users WHERE NOT EXISTS (SELECT * FROM users WHERE id = 00001)
```
pqsql在这点上真叫一个繁琐!

在Java中经常会遇到通过外部语言扩展Java本身的需要,此时就需要使用到JNI这门技术(规范)。
在c/c++中,java层的数据类型需要转化成类型签名,如在google中被收录在最前边的[官方文档](https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html)中[Table 3-2](https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp276)所示。

问题来了,那么void类型在c/c++用什么来映射呢。这份表格里边是没有的(对应java 7的[JNI规范里](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp16432)边也没有)。今天在[其他文档](http://www.rgagnon.com/javadetails/java-0286.html)里边间接查到void类型的类型签名是V:

```bash
Type Chararacter
boolean Z
byte B
char C
double D
float F
int I
long J
object L
short S
void V
array [
```
比如Java方法:
```java
public static void jsDebugDetachCallback(long udata)
```
的类型签名为(其中返回值类型V不可省略):
```java
"(J)V"
```
在c中获取该方法id的方式为:
```c
jsDebugDetachCallbackMethod = (*env)->GetStaticMethodID(env, someClz, "jsDebugDetachCallback", "(J)V");
```

最近需要使用opengrok作为webservice来提供代码交叉索引功能。

但是opengrok的release版本中还没有提供这个功能,原因在于这个提交没有被release。

所以只能自己编译最新的master分支代码并deploy。注意一点编译时需要1.8的jdk,tomcat server也需要1.8jdk来运行方能不出错。

在服务的url后边加上json?freetext=ThreadPoolExecutor之类的参数(其他参数请见我在Opengrok项目上的回复)便可查询到结果了。

针对这个问题本人贡献了一遍详细点的[文档](https://github.com/OpenGrok/OpenGrok/wiki/OpenGrok-web-services),请参阅。

最近遇到一个应用打开WebApp后,将应用压入后台,出现比较耗电的问题。集合众多同学的智慧之后定位到原因:

在WebKit的内核中会引用从各个平台(android, linux pc, mac)注入的自己的服务比如:

* DeviceOrientationService
* DeviceMotionService

而这两个Service在android平台上的实现皆通过注册侦听器到SensorManager来获知来自于加速度器,磁力计的方位、手机姿势数据。

Sensor的使用是极其浪费电力的一件事情,所以就出现了上文所述问题。

解决方法:

1) 在Activity的onPause(), onResume()时调用WebView.onPause(), WebView.onResume()以尽量将JS执行线程以及其他WebCore中的线程给停住

2) 在onStop()时将WebView中当前load的url记住,然后让WebView.loadUrl("about:blank"),在onStart()时WebView.loadUrl({your_previous_url})

更新:
[2015/11]在最近的工作中还发现一种前端页面带来的耗电问题:当页面加载一些gif或者通过其他方式创建的动画效果,当webview变得不可见后这些动画如果没有被显式关掉,就会导致很严重的耗电问题。同时在桌面浏览器里边我也注意到页面里边持续运行的动画也会导致内存占用增大。