这属于android中最常见的TransactionTooLargeException的后果表现之一。

该问题较多出现在使用PackageManager查询系统的应用的信息,通过Intent来过滤匹配目标应用组件(android四大组件)时,典型堆栈如下:

```java
java.lang.RuntimeException: Package manager has died
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:78)

......
Caused by: android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1393)
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:73)
... 17 more
android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1393)
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:73)

```
这是Java层的堆栈,原始抛出异常的地方在于文件frameworks/base/core/jni/android_util_Binder.cpp:

```c
case FAILED_TRANSACTION:
LOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
break;
```
该FAILED_TRANSACTION错误码定义于:
/bionic/libc/kernel/common/linux/binder.h中的BR_FAILED_REPLY

```c
enum BinderDriverReturnProtocol {
BR_ERROR = _IOR_BAD('r', 0, int),

BR_OK = _IO('r', 1),

BR_TRANSACTION = _IOR_BAD('r', 2, struct binder_transaction_data),
BR_REPLY = _IOR_BAD('r', 3, struct binder_transaction_data),

BR_ACQUIRE_RESULT = _IOR_BAD('r', 4, int),

BR_DEAD_REPLY = _IO('r', 5),

BR_TRANSACTION_COMPLETE = _IO('r', 6),

BR_INCREFS = _IOR_BAD('r', 7, struct binder_ptr_cookie),
BR_ACQUIRE = _IOR_BAD('r', 8, struct binder_ptr_cookie),
BR_RELEASE = _IOR_BAD('r', 9, struct binder_ptr_cookie),
BR_DECREFS = _IOR_BAD('r', 10, struct binder_ptr_cookie),

BR_ATTEMPT_ACQUIRE = _IOR_BAD('r', 11, struct binder_pri_ptr_cookie),

BR_NOOP = _IO('r', 12),

BR_SPAWN_LOOPER = _IO('r', 13),

BR_FINISHED = _IO('r', 14),

BR_DEAD_BINDER = _IOR_BAD('r', 15, void *),

BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR_BAD('r', 16, void *),

BR_FAILED_REPLY = _IO('r', 17),

};
```
其原因总结如下,首先Android系统对于Binder IPC每进程的事务buffer上限是1M(为所有Binder事务共享,且不区分目标系统的ram配置等因素),在通过PackageManager中获取系统内应用时,比如应用的Icon是个bitmap,且不同应用所制作Icon的品质(像素数)上差异很大。会占用大量内存导致Binder事务内存超过上限。

Binder事务buffer的尺寸定义在ProcessState.cpp:

```c
#define BINDER_VM_SIZE((1 * 1024 * 1024) - (4096 * 2))
......
ProcessState::ProcessState(): mDriverFD(open_driver()), mVMStart(MAP_FAILED), mManagesContexts(false), mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL), mThreadPoolStarted(false), mThreadPoolSeq(1) {
if (mDriverFD >= 0) {
// XXX Ideally, there should be a specific define for whether we
// have mmap (or whether we could possibly have the kernel module
// availabla).
#
if !defined(HAVE_WIN32_IPC)
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}#
else
mDriverFD = -1;#
endif
}

LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}
```
App icon对应的数据模型如下:

```java
class AppInfo {

......

private BitmapDrawable icon

......

}
```
该问题是android系统的限制,只能通过上层来捕获该异常来绕过。当然也不仅限于PackageManager,其他系统服务,应用间的Binder调用也可以带来这一问题。

最后的最后,由于在c++向java层抛出这个异常的场景有多种(见上边c++代码中“FAILED_TRANSACTION”处的注释),所以当你收到了TransactionTooLargeException的时候,不意味着真正的Transaction is Too Large。

最近踩了一个坑,有个非全屏的dialog遮住我的WebApp容器时,WebApp容器中JS代码操纵dom对象显示文本(input框中)巨慢(俗话描述就是“软键盘中输入了字,但是上屏很慢”)。

debug了一天之后发现。在WebView的onPause方法调用后,其内部JS引擎执行JS代码会慢好几倍。所以是否需要跟随Activity的生命周期调用onPause方法需要根据应用场景来区分:

若是应用中严格依赖JS做一些比较紧要的事情,则不应该onPause WebView。否则应该onPause WebView以释放一部分系统资源。

由于容纳 RN view 的外围控件不确定,同时又要与其他view协调好尺寸和布局。
所以 RN 对 RootView(包含了一个模块的所有布局)的处理挺有技巧,RN 中有如下调用逻辑:

ReactActivity.createRootView -> setContentView(mReactRootView)

ReactRootView.onMeasure()

ReactInstanceManagerImpl.attachMeasuredRootViewToInstance

UIManagerModule.addMeasuredRootView

得到宽高:

```java
final int width;
final int height;
// If LayoutParams sets size explicitly, we can use that. Otherwise get the size from the view.
if (rootView.getLayoutParams() != null &&
rootView.getLayoutParams().width > 0 &&
rootView.getLayoutParams().height > 0) {
width = rootView.getLayoutParams().width;
height = rootView.getLayoutParams().height;
} else {
width = rootView.getWidth();
height = rootView.getHeight();
}

rootView.setOnSizeChangedListener(
new SizeMonitoringFrameLayout.OnSizeChangedListener() {
@Override
public void onSizeChanged(final int width, final int height, int oldW, int oldH) {
getReactApplicationContext().runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
updateRootNodeSize(tag, width, height);
}
});
}
});
```
通过这个 SizeMonitoringFrameLayout.OnSizeChangedListener 将 SizeMonitoringFrameLayout(实际扮演一个容器 ViewGroup 来监听布局的改变)的宽高传递给 css-layout 用于布局各个"盒"节点。

在开发包含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)
```