标签 android 下的文章

在开发包含c/c++本地代码的android项目中,通过gdb来调试代码是必不可少的前提。

android官方为此提供了ndk-gdb,看起来非常之nice。但个人在实践中发现还是有一系列问题需要记载下(ndk版本:r10e):

1) ndk-build NDK_DEBUG=1这个选项编译时要加上,一般将之定制在你的c/c++ builder中

2) 即便你按照1)做了,在项目根目录运行ndk-gdb的时候还是会报以下错误:

ERROR: Package faywong.github.io.mediakit is not debuggable ! You can fix that in two ways:

- Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'.

- Modify your manifest to set android:debuggable attribute to "true",

then rebuild normally.

After one of these, re-install to the device!

然后我们乖乖地跑到AndroidManifest.xml里边去修改Application标签的debuggable属性,eclipse会提示你不能hardcode,可以通过如下方式设置下:

QQ20151012-1

3) 接下来还有可能会遇到如下问题:

ERROR: Could not find gdb.setup under ./libs/

这是由于ndk-gdb命令的bug带来的,它没有去参照ABI的不同设置去不同的目录下找gdb.setup文件(是一个脚本文件,帮你做一些繁琐的gdb server的启动,gdb client的启动和设置等任务)。

简单绕过这个错误的方式是将libs/{your abi, e.g. armeabi-v7a}里的gdb.setup直接拷贝至libs目录下

4) 在你跋山涉水,翻山越岭走了这么久之后,再次运行ndk-gdb,会出现以下惊喜:

ERROR: Non-debuggable application installed on the target device.

Please re-install the debuggable version!

#### 更新
现在可以结合gradle-experimental插件和ndk中搭载的lldb + android studio 2.0断点native代码了,虽然还不那么完善,bug多多,但是相比过去的ndk-gdb时代还是进步一点了。

这台红米Note1W上会有系统资源(res/drawable/btn_check_holo_light.xml)找不到的问题。备忘下。

```bash
0x10800de
java.lang.NullPointerException
at android.graphics.drawable.DrawableContainer$DrawableContainerState.addChild(DrawableContainer.java:584)
at android.graphics.drawable.StateListDrawable$StateListState.addStateSet(StateListDrawable.java:291)
at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:189)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:937)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:877)
at android.content.res.Resources.createFromXml(Resources.java:2821)
at android.content.res.Resources.loadDrawable(Resources.java:2191)
at android.content.res.Resources.loadDrawable(Resources.java:2103)
at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
at android.widget.CompoundButton.(CompoundButton.java:74)
at android.widget.CheckBox.(CheckBox.java:68)
at android.widget.CheckBox.(CheckBox.java:64)
at android.widget.CheckBox.(CheckBox.java:60)
```

mView是我们期望有动画效果的目标View

```java
FrameLayout.LayoutParams mParams = ... // 我们的目标 layout params
if (mTransitionTime > 0) {
ValueAnimator widthAnim = ValueAnimator.ofInt(0, mParams.width); // 从0变化到期望的宽度
widthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 在回调里我们踩着节拍来修改 view 的 layout params
int val = (Integer) valueAnimator.getAnimatedValue();
mParams.width = val;
if (mView != null) {
mView.setLayoutParams(mParams);
}
}
});
widthAnim.setDuration(mTransitionTime);
widthAnim.start();

ValueAnimator heightAnim = ValueAnimator.ofInt(0, mParams.height); // 从0变化到期望的高度
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 在回调里我们踩着节拍来修改 view 的 layout params
int val = (Integer) valueAnimator.getAnimatedValue();
mParams.height = val;
if (mView != null) {
mView.setLayoutParams(mParams);
}
}
});
heightAnim.setDuration(mTransitionTime);
heightAnim.start();
}

if (mTransformDegree > 0) { // 如果需要,在由小变大的过程中添加旋转效果
ObjectAnimator.ofFloat(mView, "rotation", 0f, mTransformDegree * 1.0f)
.setDuration(mTransitionTime > 0 ? mTransitionTime : 0).start();
}
```

由于MEIZU手机的特殊性,对于NavigationBar的获取有特殊的讲究,有三个方面的因素:

1)MEIZU早期的系统(flyme os 5以下)存在smartbar,且可以设置智能隐藏;flyme os 5及以后smartbar被废弃,而使用android的NavigationBar

2)flymeos 5以下的系统有些存在系统dimen资源用于获取smartbar高度,有些则不再存在

3)是否智能隐藏SmartBar的开关并不与Android是否显示NavigationBar的开关为同一个

所以我今天研究了下,封装了一个方法:

```java
public static int getNavigationBarHeight(Context context) {
final boolean isMeiZu = Build.MANUFACTURER.equals("Meizu");

final boolean autoHideSmartBar = Settings.System.getInt(context.getContentResolver(),
"mz_smartbar_auto_hide", 0) == 1;

if (isMeiZu) {
if (autoHideSmartBar) {
return 0;
} else {
try {
Class c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("mz_action_button_min_height");
int height = Integer.parseInt(field.get(obj).toString());
return context.getResources().getDimensionPixelSize(height);
} catch (Throwable e) { // 不自动隐藏smartbar同时又没有smartbar高度字段供访问,取系统navigationbar的高度
return getNormalNavigationBarHeight(context);
}
}
} else {
return getNormalNavigationBarHeight(context);
}
}

protected static int getNormalNavigationBarHeight(final Context ctx) {
try {
final Resources res = ctx.getResources();
int rid = res.getIdentifier("config_showNavigationBar", "bool", "android");
if (rid > 0) {
boolean flag = res.getBoolean(rid);
if (flag) {
int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return res.getDimensionPixelSize(resourceId);
}
}
}
} catch (Throwable e) {
LogCatLog.d("FBTools", "getNormalNavigationBarHeight() exception:" + e.getMessage());
}
return 0;
}
```

在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)