在Android系统中,Binder传输的数据大小是有限制的。在Binder机制中,数据传输的限制主要取决于两个方面:一是Binder缓冲区大小,二是Binder transaction的大小限制。
Binder缓冲区大小是指Binder在内核空间中用于存储传输数据的缓冲区大小。在Android系统中,Binder缓冲区大小通常为4KB。这意味着在进行Binder通信时,每次传输的数据不能超过4KB。如果传输的数据大小超过了4KB,系统会将数据分成多个4KB的块进行传输。
另一个限制是Binder transaction的大小限制。Binder transaction是指在Binder通信中一次传输的数据量。在Android系统中,Binder transaction的大小限制通常为1MB。这意味着在进行Binder通信时,一次传输的数据不能超过1MB。如果需要传输更大的数据量,可以通过分多次传输的方式来实现。
因此使用共享内存是很好的方式。在内存中开辟一块空间,通过binder或者其他方式将fd(文件描述符)传递到客户端或服务端进程,从而实现大文件传输。
Android 的 匿名共享内存(Ashmem) 基于 Linux 的共享内存,都是在临时文件系统(tmpfs)上创建虚拟文件,再映射到不同的进程。它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。相对于 Linux 的共享内存,Ashmem 对内存的管理更加精细化,并且添加了互斥锁。Java 层在使用时需要用到 MemoryFile,它封装了 native 代码。
Java 层使用匿名共享内存的4个点:
通过 MemoryFile 开辟内存空间,获得 FileDescriptor;
将 FileDescriptor 传递给其他进程;
往共享内存写入数据;
从共享内存读取数据。
关于 匿名共享内存 和 mmap 的关系
MemoryFile 在底层是调用了 mmap 将内存映射在了一个虚拟文件上。 两个进程通过 MemoryFile 访问这个虚拟文件对应的内存。
举例实现
一,服务端
首先在APP内部存储中存放一张图片:路径 data/data/com.example.ashmem_service/files/image.jpg
将文件写入到共享内存,并返回ParcelFileDescriptor
通过binder将返回的pfd传递给客户端
aidl文件
// IMyAidlInterface.aidl package com.example.ashmem; // Declare any non-default types here with import statements interface IMyAidlInterface { ParcelFileDescriptor getPfd(); }
service文件
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); } IMyAidlInterface.Stub myAidlInterface=new IMyAidlInterface.Stub() { @Override public ParcelFileDescriptor getPfd() throws RemoteException { return createMemory(); } }; @Nullable @Override public IBinder onBind(Intent intent) { return myAidlInterface; } //创建共享内存区域 private ParcelFileDescriptor createMemory() { MemoryFile memoryFile=null; byte[] bytes =readFile(new File(getFilesDir().getPath(),"image.jpg")); try { //创建内存对象 参数一:name 参数二:开辟的内存大小 memoryFile=new MemoryFile("test_memory",1024*4); //将数据写入到内存当中 memoryFile.getOutputStream().write(bytes); //通过反射获取到getFileDescriptor方法,注意 Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor"); //得到文件描述符 FileDescriptor fd = (FileDescriptor) method.invoke(memoryFile); //将文件描述符返回 return ParcelFileDescriptor.dup(fd); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } //file文件读取成byte[] public static byte[] readFile(File file) { RandomAccessFile rf = null; byte[] data = null; try { rf = new RandomAccessFile(file, "r"); data = new byte[(int) rf.length()]; rf.readFully(data); } catch (Exception exception) { exception.printStackTrace(); } finally { closeQuietly(rf); } return data; } //关闭读取file public static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (Exception exception) { exception.printStackTrace(); } } }
客户端
拿到fd写入到文件中即可:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; IMyAidlInterface aidl; ParcelFileDescriptor pfd = null; private ServiceConnection connection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String filePath = this.getFilesDir().getPath(); File file = new File(filePath + "/image.jpg"); connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { aidl = IMyAidlInterface.Stub.asInterface(service); try { pfd = aidl.getPfd(); FileDescriptor fileDescriptor = pfd.getFileDescriptor(); FileInputStream inputStream=new FileInputStream(fileDescriptor); file.createNewFile(); FileOutputStream fo = new FileOutputStream(file); int read = inputStream.read(); while (read != -1) { fo.write(read); read = inputStream.read(); } //关闭流 fo.flush(); fo.close(); inputStream.close(); // Log.i(TAG,"接收到的数据:"+new String(content,"UTF-8")); } catch (RemoteException | IOException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; findViewById(R.id.bindService).setOnClickListener(this); } private void bindService() { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.ashmem", "com.example.ashmem.MyService")); boolean isbind = bindService(intent, connection, Context.BIND_AUTO_CREATE); Log.i(TAG, "bindService: "+isbind); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bindService: bindService(); break; default: break; } } }
- 文章2305
- 用户1336
- 访客11456237
没有努力,天份不代表什么。
MySQL 数据库优化
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
免ROOT实现模拟点击任意位置
Mobaxterm终端神器
CreateProcessW要注意的细节问题
Autonomous NAT Traversal
【教程】win10 彻底卸载edge浏览器
eclipse工程基于Xposed的一个简单Hook
排名前5的开源在线机器学习
Mac OS最简单及(Karabiner)快捷键设置
发一款C++编写的麻将
VMware NAT端口映射外网访问虚拟机linux
独家发布最新可用My-AutoPost——wordpress 采集器