面试官提了一个问题,我们来看看 小菜、阿宅 和 大神 三位同学的表现如何吧


小菜 自认为无所不知,水平已达应用开发天花板,目前月薪 10k

面试官:如何跨进程传递大图

小菜:很简单,把图片存到 SD 卡,然后把路径传过去,在别的进程读出来这不就完事了嘛。

面试官:这个需要文件操作,效率不行,有别的方法吗?

小菜:Bitmap 实现了 Parcelable 接口,可以通过 Intent.putExtra(String name, Parcelable value) 方法直接放 Intent 里面传递。

面试官:那这个方法有什么缺点?

小菜:Bitmap 太大就会抛异常,所以我更喜欢传路径

面试官:为什么会抛异常?

小菜:.....

面试官:好的,回去等通知吧


阿宅 业余时间经常打游戏、追剧、熬夜,目前月薪 15k

面试官:Intent 直接传 Bitmap 会有什么问题?

阿宅:Bitmap 太大会抛 TransactionTooLargeException 异常,原因是:底层判断只要 Binder Transaction 失败,且 Intent 的数据大于 200k 就会抛这个异常了。(见:android_util_Binder.cpp 文件 signalExceptionForError 方法)

面试官:为什么 Intent 传值会有大小限制。

阿宅:应用进程在启动 Binder 机制时会映射一块 1M 大小的内存,所有正在进行的 Binder 事务共享这 1M 的缓冲区 。当使用 Intent 进行 IPC 时申请的缓存超过 1M - 其他事务占用的内存时,就会申请失败抛 TransactionTooLargeException 异常了。 (哼,不会像上次一样答不出来了。见:“谈谈你对 binder 的理解?这样回答才过关”)

面试官:如何绕开这个限制呢?

阿宅:通过 AIDL 使用 Binder 进行 IPC 就不受这个限制,具体代码如下:

Bundle bundle = new Bundle();
bundle.putBinder("binder", new IRemoteGetBitmap.Stub() {@Overridepublic Bitmap getBitMap() throws RemoteException {return mBitmap;}
});
intent.putExtras(bundle);

面试官:这是什么原理呢?

阿宅:还没去细看

面试官:好的,回去等通知吧


大神 坚持每天学习、不断的提升自己,目前月薪 30k

面试官:为什么通过 putBinder 的方式传 Bitmap 不会抛 TransactionTooLargeException 异常

大神:这个问题,我们先来看下,底层在 IPC 时是怎么把 Bitmap 写进 Parcel 的。

Android - 28 Bitmap.cpp
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, ...) {// 拿到 Native 的 Bitmap                                auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);// 拿到其对应的 SkBitmap, 用于获取 Bitmap 的像素信息bitmapWrapper->getSkBitmap(&bitmap);int fd = bitmapWrapper->bitmap().getAshmemFd();if (fd >= 0 && !isMutable && p->allowFds()) {// Bitmap 带了 ashmemFd && Bitmap 不可修改 && Parcel 允许带 fd// 就直接把 FD 写到 Parcel 里,结束。status = p->writeDupImmutableBlobFileDescriptor(fd);return JNI_TRUE;}// 不满足上面的条件就要把 Bitmap 拷贝到一块新的缓冲区android::Parcel::WritableBlob blob;// 通过 writeBlob 拿到一块缓冲区 blobstatus = p->writeBlob(size, mutableCopy, &blob);// 获取像素信息并写到缓冲区const void* pSrc =  bitmap.getPixels();if (pSrc == NULL) {memset(blob.data(), 0, size);} else {memcpy(blob.data(), pSrc, size);}
}

接下来我们看一下 writeBlob 是怎么获取缓冲区的(注意虽然方法名写着 write , 但是实际往缓冲区写数据是在这个方法执行之后)

Android - 28 Parcel.cpp
// Maximum size of a blob to transfer in-place.
static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{if (!mAllowFds || len <= BLOB_INPLACE_LIMIT) {// 如果不允许带 fd ,或者这个数据小于 16K// 就直接在 Parcel 的缓冲区里分配一块空间来保存这个数据status = writeInt32(BLOB_INPLACE);void* ptr = writeInplace(len);outBlob->init(-1, ptr, len, false);return NO_ERROR;}// 另外开辟一个 ashmem,映射出一块内存,后续数据将保存在 ashmem 的内存里int fd = ashmem_create_region("Parcel Blob", len);void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);...// parcel 里只写个 fd 就好了,这样就算数据量很大,parcel 自己的缓冲区也不用很大status = writeFileDescriptor(fd, true /*takeOwnership*/);outBlob->init(fd, ptr, len, mutableCopy);return status;
}

通过上面的分析,我们可以看出,同一个 Bitmap 写入到 Parcel 所占的缓冲区大小和 Pacel 的 allowFds 有关。

直接通过 Intent 传 Bitmap 容易抛 TransactionTooLargeException 异常,就是因为 Parcel 的 allowFds = false,直接把 Bitmap 写入缓冲区占用了较大的内存。

接下来,我们来看一下,allowFds 是什么时候被设置成 false 的呢:

// 启动 Activity 执行到 Instrumentation.java 的这个方法
public ActivityResult execStartActivity(..., Intent intent, ...){...intent.prepareToLeaveProcess(who);ActivityManager.getService().startActivity(...,intent,...)
}// Intent.java
public void prepareToLeaveProcess(boolean leavingPackage) {// 这边一层层传递到最后设置 Parcel 的 allowfdssetAllowFds(false);....
}

面试官:太多了,你懂的。

大神:总结一下:较大的 bitmap 直接通过 Intent 传递容易抛异常是因为 Intent 启动组件时,系统禁掉了文件描述符 fd 机制 , bitmap 无法利用共享内存,只能拷贝到 Binder 映射的缓冲区,导致缓冲区超限, 触发异常; 而通过 putBinder 的方式,避免了 Intent 禁用描述符的影响,bitmap 写 parcel 时的 allowFds 默认是 true , 可以利用共享内存,所以能高效传输图片。

面试官:可以,我们再来聊聊别的。


文章首发于公众号:“Android 面试官”,未经授权请勿转载。

扫一扫关注公众号,为你解答 Android 面试的各种问题

gin.context 怎么在其他包中获取 只能传递吗_跨进程传递大图,你能想到哪些方案呢?...相关推荐

  1. java开发_mysql中获取数据库表描述_源码下载

    功能描述: 在mysql数据库中,有两张表: data_element_config , test_table 我们需要获取表:test_table表的描述信息,然后把描述信息插入到表:data_el ...

  2. struts2在action中获取request、session、application,并传递数据

    假设仅仅是通过request.session.application传递数据,则不须要获取对应的对象也能够传递数据,代码例如以下: ScopeAction.java: package com.ithe ...

  3. java中获取文件总行数_关于java:如何以有效的方式获取文件中的行数?

    本问题已经有最佳答案,请猛点这里访问. 我有一个大文件. 它包括大约3.000-20.000行. 如何使用Java获取文件中的行总数? 从你的评论到答案来判断,你要找的词是"有效的" ...

  4. mysql中获取时间的年月日_关于苹果ios中的Date()获取时间NaN的问题

    项目开发过程中难免会遇到倒计时,获取时间等类似的问题,然而ios端获取时间戳时发现,显示结果NaN(nont a number),安卓端显示是ok的. ***原因:Date()内时间格式问题*** i ...

  5. c中获取python控制台输出_在真实的tim中用C捕获控制台python打印

    我正在尝试从C创建一个python进程,并从python脚本获取打印结果.在 这就是我的C代码:namespace ConsoleApp1 { public class CreateProcess { ...

  6. mysql中获取时间的年月日_详解mysql 获取当前日期及格式化

    MySQL 获取当前日期及日期格式 获取系统日期: NOW() 格式化日期: DATE_FORMAT(date, format) 注: date:时间字段 format:日期格式 返回系统日期,输出 ...

  7. java数组中获取长度通过调用_数组的长度如何获取?

    获取数组长度的方法: 1.在java与JavaScript中可以使用"数组名.length"方法获取数组长度 javaScriptvar arr=new Array(); arr[ ...

  8. java中获取文本域内容_怎样读文件内容到文本域中(java SWT)

    //写了段简单的代码提供你参考importjava.io.BufferedReader;importjava.io.File;importjava.io.FileInputStream;importj ...

  9. java 共享内存获取写权限_跨进程访问共享内存的权限问题

    问:我在服务器上用 CreateFileMapping 创建了一段共享内存.让这个exe始终在服务器上跑. 同时,别的用户在客户端用IE访问服务器,将要查询的数据通过C#制作的网页提交上来,服务器得到 ...

最新文章

  1. php 魔术变量和超级全局变量,PHP超级全局变量与魔术变量
  2. 计算机病毒会不会通过u盘传染,这样会感染病毒吗?
  3. 毕业一两年,怎样快速成长和晋升?
  4. VTK:可编程字形过滤器用法实战
  5. 业务决定功能,功能决定技术
  6. 乐高(LEGO)在线购物店面剖析
  7. maven,gradle本地缓存位置
  8. 运算符优先级 速查表
  9. C++ Gui Qt4 书籍代码在VS2008上正确编译使用
  10. Mobx入门之四:自定义reactions,when, autorun
  11. 我所熟悉的网站负载均衡技术之硬件篇
  12. Struts1和Struts2的特点、工作流程及差异性
  13. Atitit 物联网之道 艾龙著 attilax著 1. 理论基础(控制理论 信息理论) 2 2. 1.5 物联网的关键技术12 2 2.1. 1.5.1 网络与通信技术12 1.5.2 无线传感
  14. windows系统查看局域网内所有已使用的IP
  15. icon 的css,【iview】icon样式
  16. QPS,TPS,RT是什么?
  17. 放弃了又何需执着 ?
  18. 微信小程序左滑删除(Slideview)
  19. linux 局域网 提高网速,提高Linux系统网速的方法
  20. DNS域传送漏洞--vulhub复现

热门文章

  1. R语言使用psych包的fa函数对指定数据集进行因子分析(输入数据为相关性矩阵)、使用rotate参数指定进行斜交旋转提取因子、并获取因子分数、因子得分系数(scoring coefficients)
  2. R语言可视化斜率图、扩充图像纵横比为数据标签显示更整齐、ggrepel包来帮忙
  3. pandas使用apply函数将dataframe多个数据列整合为元组形式并生成新的数据列(combine multiple columns as a single column of tuples)
  4. pandas使用isna函数和any函数计算返回dataframe中包含缺失值的数据行(rows with missing values in dataframe)
  5. R语言ggplot2可视化:ggplot2可视化使用guide_axis(check.overlap=TRUE)选项删除重叠的轴文本、跳过部分中间轴标签
  6. R语言do.call函数和call函数实战
  7. KL变换+PCA+关系
  8. agv调度matlab程序,一种分布式AGV调度方法及调度系统与流程
  9. ML基石_2_LearnAnswer2
  10. LINUX的20练习题