Android Binder传输大文件

问题和解决方案

想要在自定义安卓系统服务中传输较大的数据,由于系统服务使用binder进行IPC,而binder最大只能传输(1mb-8kb)的数据,目前想到的解决方案是把数据拆分为多份,然后多次传输。本文记录这个解决方案遇到的一些坑

先放上关键的代码:

auto myService = aidl::android::MyService::fromBinder(ndk::SpAIBinder(AServiceManager_getService("myservice")));
int pack_size = 0x7E000;  // 必须为4kb整数倍,且长度最好在1mb-8kb的一半以内
int count = (size / pack_size); // size是数据长度
if(count >= 255){
    return false;
}

// 分块传输, binder限制,每次只能传输1mb不到的数据
for(int i=0; i < count; i++){
    // begin是要传输的数据的首地址, size是数据长度
    std::vector<uint8_t> buff(begin + i*pack_size, begin + (i+1)*pack_size);
    // 传输pid、begin等参数,用于标记具体数据来源,传输参数i,用于标记块的顺序,方便服务端进行文件重组
    myService->trans_data(pid, begin, size, i, buff);
    usleep(5); //第二章会说明为什么要sleep
}
std::vector<uint8_t> buff(begin + count*pack_size, begin + size);
// 这里别忘了把额外的数据也补齐到4kb的整数倍,这里略过了补齐的代码
myService->trans_data(pid, begin, size, count, buff);
usleep(5);

数据必须为4kb的整数倍

刚开始我把数据拆分为每块1000000byte,可是服务端只接收到了999424byte数据,接收到的部分数据我保存为文件之后,如下所示:

-rw-rw---- 1 root root 999424 2023-08-09 16:24 part_2
-rw-rw---- 1 root root 999424 2023-08-09 16:24 part_3
-rw-rw---- 1 root root 999424 2023-08-09 16:24 part_4
-rw-rw---- 1 root root 999424 2023-08-09 16:24 part_5

999424换成16进制,就是0xF4000,是4kb的整数倍,这里我没有进一步确认是vector的问题还是binder的问题(也不知道为啥要舍弃掉多余的字符,而不是填充0补齐到整数倍,想弄懂原理估计很麻烦,这里先放着了),总之先把每块的数据调整为4kb的倍数。

调整后客户端发送的和服务端接收到的数据都是999424byte,保持一致,这个问题解决。

buffer释放问题

服务端处理数据的速度过慢时,会导致buffer来不及释放的问题。

比如每次只传999504byte数据,此时一个binder的buffer还会剩下40880byte内存,如果下一条数据已经准备开始传输了,同时上一条数据还没有处理完,buffer没有释放,这时只能在剩下的40880byte内存中申请999504byte的数据,很明显是不够的

注:我每块大小为999424byte,但是log显示要申请999504byte数据,猜测是其他的参数或者某些额外的数据结构多占了部分空间,这个对我最终目标没影响所以也不过多研究了

仔细观察报错,可以发现正常传输数据的间隔大概在3ms,而报错的地方与上一条log只差了1ms,于是修改代码,在每次循环中加入5ms的等待时间

15:46:26.473  1258  1310 W         ==Mytag==
15:46:26.476  1258  1310 W         ==Mytag==
15:46:26.475     0     0 E         : c4   5038 binder_alloc: 1258: binder_alloc_buf size 999504 failed, no address space
15:46:26.475     0     0 E         : c4   5038 binder_alloc: allocated: 999504 (num: 1 largest: 999504), free: 40880 (num: 1 largest: 40880)
15:46:26.475     0     0 I         : c4   5038 binder: 5038:5038 transaction failed 29201/-28, size 999504-0 line 3317
15:46:26.480  1258  1310 W         ==Mytag==
15:46:26.483  1258  1310 W         ==Mytag==
15:46:26.486  1258  1310 W         ==Mytag==

但是修改之后还是会报同样的错,只不过频率低了很多,连续多次传输只报了一次错误,没修改之前几乎每次都会报错。而且观察修改后的log,其中的Mytag的时间间隔依旧在3ms,那么剩下的问题肯定不在我们客户端的数据传输间隔上了

重点转移到服务端,众所周知,binder服务端是可以多线程处理任务的,binder多个线程是共用一块buffer内存,所以减少多线程数量,即可减少多个线程争抢一块内存的情况。我这里用的是android系统服务的ndk后端,使用ABinderProcess_setThreadPoolMaxThreadCount 来设置最大线程数,我这里设定为5,但是依旧会出现上面的报错。

最后想到一个办法:把每块的大小限制在(1mb-8kb)的一半以内(一半是0x7F000,我最终设定为0x7E000),那么即使出现了上面没有及时释放buffer的情况,剩下的一半空间也能够支持一次数据传输。在循环加入等待时间之后,出现报错的频率已经大幅减少,在加上大小限制在一半的操作,最后终于稳定的通过了测试,多轮数据传输测试下来,基本没有再出现过报错了。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值