gin.context 怎么在其他包中获取 只能传递吗_跨进程传递大图,你能想到哪些方案呢?...
面试官提了一个问题,我们来看看 小菜、阿宅 和 大神 三位同学的表现如何吧
小菜 自认为无所不知,水平已达应用开发天花板,目前月薪 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 面试官”,未经授权请勿转载。
gin.context 怎么在其他包中获取 只能传递吗_跨进程传递大图,你能想到哪些方案呢?...相关推荐
- java开发_mysql中获取数据库表描述_源码下载
功能描述: 在mysql数据库中,有两张表: data_element_config , test_table 我们需要获取表:test_table表的描述信息,然后把描述信息插入到表:data_el ...
- struts2在action中获取request、session、application,并传递数据
假设仅仅是通过request.session.application传递数据,则不须要获取对应的对象也能够传递数据,代码例如以下: ScopeAction.java: package com.ithe ...
- java中获取文件总行数_关于java:如何以有效的方式获取文件中的行数?
本问题已经有最佳答案,请猛点这里访问. 我有一个大文件. 它包括大约3.000-20.000行. 如何使用Java获取文件中的行总数? 从你的评论到答案来判断,你要找的词是"有效的" ...
- mysql中获取时间的年月日_关于苹果ios中的Date()获取时间NaN的问题
项目开发过程中难免会遇到倒计时,获取时间等类似的问题,然而ios端获取时间戳时发现,显示结果NaN(nont a number),安卓端显示是ok的. ***原因:Date()内时间格式问题*** i ...
- c中获取python控制台输出_在真实的tim中用C捕获控制台python打印
我正在尝试从C创建一个python进程,并从python脚本获取打印结果.在 这就是我的C代码:namespace ConsoleApp1 { public class CreateProcess { ...
- mysql中获取时间的年月日_详解mysql 获取当前日期及格式化
MySQL 获取当前日期及日期格式 获取系统日期: NOW() 格式化日期: DATE_FORMAT(date, format) 注: date:时间字段 format:日期格式 返回系统日期,输出 ...
- java数组中获取长度通过调用_数组的长度如何获取?
获取数组长度的方法: 1.在java与JavaScript中可以使用"数组名.length"方法获取数组长度 javaScriptvar arr=new Array(); arr[ ...
- java中获取文本域内容_怎样读文件内容到文本域中(java SWT)
//写了段简单的代码提供你参考importjava.io.BufferedReader;importjava.io.File;importjava.io.FileInputStream;importj ...
- java 共享内存获取写权限_跨进程访问共享内存的权限问题
问:我在服务器上用 CreateFileMapping 创建了一段共享内存.让这个exe始终在服务器上跑. 同时,别的用户在客户端用IE访问服务器,将要查询的数据通过C#制作的网页提交上来,服务器得到 ...
最新文章
- php 魔术变量和超级全局变量,PHP超级全局变量与魔术变量
- 计算机病毒会不会通过u盘传染,这样会感染病毒吗?
- 毕业一两年,怎样快速成长和晋升?
- VTK:可编程字形过滤器用法实战
- 业务决定功能,功能决定技术
- 乐高(LEGO)在线购物店面剖析
- maven,gradle本地缓存位置
- 运算符优先级 速查表
- C++ Gui Qt4 书籍代码在VS2008上正确编译使用
- Mobx入门之四:自定义reactions,when, autorun
- 我所熟悉的网站负载均衡技术之硬件篇
- Struts1和Struts2的特点、工作流程及差异性
- Atitit 物联网之道 艾龙著 attilax著 1. 理论基础(控制理论 信息理论)	2 2. 1.5 物联网的关键技术12 	2 2.1. 1.5.1 网络与通信技术12 1.5.2 无线传感
- windows系统查看局域网内所有已使用的IP
- icon 的css,【iview】icon样式
- QPS,TPS,RT是什么?
- 放弃了又何需执着 ?
- 微信小程序左滑删除(Slideview)
- linux 局域网 提高网速,提高Linux系统网速的方法
- DNS域传送漏洞--vulhub复现
热门文章
- R语言使用psych包的fa函数对指定数据集进行因子分析(输入数据为相关性矩阵)、使用rotate参数指定进行斜交旋转提取因子、并获取因子分数、因子得分系数(scoring coefficients)
- R语言可视化斜率图、扩充图像纵横比为数据标签显示更整齐、ggrepel包来帮忙
- pandas使用apply函数将dataframe多个数据列整合为元组形式并生成新的数据列(combine multiple columns as a single column of tuples)
- pandas使用isna函数和any函数计算返回dataframe中包含缺失值的数据行(rows with missing values in dataframe)
- R语言ggplot2可视化:ggplot2可视化使用guide_axis(check.overlap=TRUE)选项删除重叠的轴文本、跳过部分中间轴标签
- R语言do.call函数和call函数实战
- KL变换+PCA+关系
- agv调度matlab程序,一种分布式AGV调度方法及调度系统与流程
- ML基石_2_LearnAnswer2
- LINUX的20练习题