Android跨进程传大图思考及实现——附上原理分析

1.抛一个问题

这一天,法海想锻炼小青的定力,由于Bitmap也是一个Parcelable类型的数据,法海想通过Intent小青传个特别大的图片

intent.putExtra("myBitmap",fhBitmap)

如果“法海”(Activity)使用Intent去传递一个大的Bitmap“小青”(Activity),如果你的图片够大,会出现类似下面这样的错误,请继续往下看:

Caused by: android.os.TransactionTooLargeException: data parcel size 8294952 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(BinderProxy.java:535)
        at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3904)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1738)

至于是什么样的大图,这个只有法海知道了(小青:好羞涩啊)🙈🙈🙈

所以TransactionTooLargeException这玩意爆出来的地方在哪呢?

2.问题定位分析

我们可以看到错误的日志信息里面看到调用了BinderProxy.transactNative,这个transactNative是一个native方法

//android.os.BinderProxy
/**
 * Native implementation of transact() for proxies
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

Android Code Search,全局搜索一下:android_os_BinderProxy_transact

//frameworks/base/core/jni/android_util_Binder.cpp

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    ......
    status_t err = target->transact(code, *data, reply, flags);
    ......
    if (err == NO_ERROR) { 
        //如果匹配成功直接拦截不往下面执行了
        return JNI_TRUE;
    } else if (err == UNKNOWN_TRANSACTION) {
        return JNI_FALSE;
    }
    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
    return JNI_FALSE;
}

我们打开signalExceptionForError方法看看里面的内容

//frameworks/base/core/jni/android_util_Binder.cpp
//处理异常的方法
void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
        bool canThrowRemoteException, int parcelSize)
{
    switch (err) {
        //其他异常,大家可以自行阅读了解;
        //如:没有权限异常,文件太大,错误的文件描述符,等等;
        ........
        case FAILED_TRANSACTION: {
            const char* exceptionToThrow;
            char msg[128];
            //官方在FIXME中写道:事务过大是FAILED_TRANSACTION最常见的原因
            //但它不是唯一的原因,Binder驱动可以返回 BR_FAILED_REPLY
            //也有其他原因可能是:事务格式不正确,文件描述符FD已经被关闭等等

            //parcelSize大于200K就会报错,canThrowRemoteException传递进来的是true
            if (canThrowRemoteException && parcelSize > 200*1024) {
                // bona fide large payload
                exceptionToThrow = "android/os/TransactionTooLargeException";
                snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
            } else {
                ..........
            }
            //使用指定的类和消息内容抛出异常
            jniThrowException(env, exceptionToThrow, msg);
        } break;
        ........
    }
}

此时我们看到: parcelSize大于200K就会报错,难道一定是200K以内?先别着急着下结论,继续往下看👇👇

3.提出疑问

法海:我有个疑问,我看到文档写的1M大小啊;

许仙:别急,妹夫,来先看一下文档的解释,看一下使用说明:
官方TransactionTooLargeException的文档中描述到:Binder 事务缓冲区有一个有限的固定大小,目前为 1MB,由进程所有正在进行的事务共享
可以看到写的是:共享事务的缓冲区

如来佛祖:汝等别急,我们简单测试一下,Intent传递201*1024个字节数组,我们发现可以正常传递过去,Logcat仅仅输出了一个Error提示的日志信息,还是可以正常传递的

E/ActivityTaskManager: Transaction too large, intent: Intent { cmp=com.melody.test/.SecondActivity (has extras) }, extras size: 205848, icicle size: 0

我们再测试一个值,intent传递800*1024个字节数组,我们发现会崩溃

android.os.TransactionTooLargeException: data parcel size 821976 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(BinderProxy.java:540)
        at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值