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)

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

```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");
```