Android Binder传输大文件

0 问题和解决方案

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

先放上关键的代码:

c++ 复制代码
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);

1 数据必须为4kb的整数倍

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

diff 复制代码
-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,保持一致,这个问题解决。

2 buffer释放问题

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

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

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

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

yaml 复制代码
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的情况,剩下的一半空间也能够支持一次数据传输。在循环加入等待时间之后,出现报错的频率已经大幅减少,在加上大小限制在一半的操作,最后终于稳定的通过了测试,多轮数据传输测试下来,基本没有再出现过报错了。

相关推荐
雨白21 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk21 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING1 天前
RN容器启动优化实践
android·react native
恋猫de小郭1 天前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker1 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴1 天前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭2 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab2 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe2 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos