当前位置:   article > 正文

Android进程间通信之共享内存的使用_android 共享内存

android 共享内存

前言

Android 提供了几种进程间通信的方式,除了Socket,基本都是基于binder实现的。为什么要用共享内存来实现呢?因为binder传输数据被限制在1M-8k,在较大的数据交换一般会使用文件,但效率非常的低,因此使用共享内存是很好的方式。在内存中开辟一块空间,通过binder或者其他方式将fd(文件描述符)传递到客户端或服务端进程,从而实现大文件传输。

原理

Android 的 匿名共享内存(Ashmem) 基于 Linux 的共享内存,都是在临时文件系统(tmpfs)上创建虚拟文件,再映射到不同的进程。它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。相对于 Linux 的共享内存,Ashmem 对内存的管理更加精细化,并且添加了互斥锁。Java 层在使用时需要用到 MemoryFile,它封装了 native 代码。

Java 层使用匿名共享内存的4个点:

  • 通过 MemoryFile 开辟内存空间,获得 FileDescriptor;
  • 将 FileDescriptor 传递给其他进程;
  • 往共享内存写入数据;
  • 从共享内存读取数据。

关于 匿名共享内存 和 mmap 的关系

MemoryFile 在底层是调用了 mmap 将内存映射在了一个虚拟文件上。 两个进程通过 MemoryFile 访问这个虚拟文件对应的内存。

举例实现

一,服务端

  1. 首先在APP内部存储中存放一张图片:路径 data/data/com.example.ashmem_service/files/image.jpg
  2. 将文件写入到共享内存,并返回ParcelFileDescriptor
  3. 通过binder将返回的pfd传递给客户端

aidl文件

  1. // IMyAidlInterface.aidl
  2. package com.example.ashmem;
  3. // Declare any non-default types here with import statements
  4. interface IMyAidlInterface {
  5. ParcelFileDescriptor getPfd();
  6. }
  1. public class MyService extends Service {
  2. @Override
  3. public void onCreate() {
  4. super.onCreate();
  5. }
  6. IMyAidlInterface.Stub myAidlInterface=new IMyAidlInterface.Stub() {
  7. @Override
  8. public ParcelFileDescriptor getPfd() throws RemoteException {
  9. return createMemory();
  10. }
  11. };
  12. @Nullable
  13. @Override
  14. public IBinder onBind(Intent intent) {
  15. return myAidlInterface;
  16. }
  17. //创建共享内存区域
  18. private ParcelFileDescriptor createMemory() {
  19. MemoryFile memoryFile=null;
  20. byte[] bytes =readFile(new File(getFilesDir().getPath(),"image.jpg"));
  21. try {
  22. //创建内存对象 参数一:name 参数二:开辟的内存大小
  23. memoryFile=new MemoryFile("test_memory",1024*4);
  24. //将数据写入到内存当中
  25. memoryFile.getOutputStream().write(bytes);
  26. //通过反射获取到getFileDescriptor方法,注意
  27. Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
  28. //得到文件描述符
  29. FileDescriptor fd = (FileDescriptor) method.invoke(memoryFile);
  30. //将文件描述符返回
  31. return ParcelFileDescriptor.dup(fd);
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. } catch (NoSuchMethodException e) {
  35. e.printStackTrace();
  36. } catch (IllegalAccessException e) {
  37. e.printStackTrace();
  38. } catch (InvocationTargetException e) {
  39. e.printStackTrace();
  40. }
  41. return null;
  42. }
  43. //file文件读取成byte[]
  44. public static byte[] readFile(File file) {
  45. RandomAccessFile rf = null;
  46. byte[] data = null;
  47. try {
  48. rf = new RandomAccessFile(file, "r");
  49. data = new byte[(int) rf.length()];
  50. rf.readFully(data);
  51. } catch (Exception exception) {
  52. exception.printStackTrace();
  53. } finally {
  54. closeQuietly(rf);
  55. }
  56. return data;
  57. }
  58. //关闭读取file
  59. public static void closeQuietly(Closeable closeable) {
  60. try {
  61. if (closeable != null) {
  62. closeable.close();
  63. }
  64. } catch (Exception exception) {
  65. exception.printStackTrace();
  66. }
  67. }
  68. }

二,客户端

拿到fd写入到文件中即可:

  1. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  2. private static final String TAG = "MainActivity";
  3. IMyAidlInterface aidl;
  4. ParcelFileDescriptor pfd = null;
  5. private ServiceConnection connection;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. String filePath = this.getFilesDir().getPath();
  11. File file = new File(filePath + "/image.jpg");
  12. connection = new ServiceConnection() {
  13. @Override
  14. public void onServiceConnected(ComponentName name, IBinder service) {
  15. aidl = IMyAidlInterface.Stub.asInterface(service);
  16. try {
  17. pfd = aidl.getPfd();
  18. FileDescriptor fileDescriptor = pfd.getFileDescriptor();
  19. FileInputStream inputStream=new FileInputStream(fileDescriptor);
  20. file.createNewFile();
  21. FileOutputStream fo = new FileOutputStream(file);
  22. int read = inputStream.read();
  23. while (read != -1) {
  24. fo.write(read);
  25. read = inputStream.read();
  26. }
  27. //关闭流
  28. fo.flush();
  29. fo.close();
  30. inputStream.close();
  31. // Log.i(TAG,"接收到的数据:"+new String(content,"UTF-8"));
  32. } catch (RemoteException | IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. @Override
  37. public void onServiceDisconnected(ComponentName name) {
  38. }
  39. };
  40. findViewById(R.id.bindService).setOnClickListener(this);
  41. }
  42. private void bindService() {
  43. Intent intent = new Intent();
  44. intent.setComponent(new ComponentName("com.example.ashmem", "com.example.ashmem.MyService"));
  45. boolean isbind = bindService(intent, connection, Context.BIND_AUTO_CREATE);
  46. Log.i(TAG, "bindService: "+isbind);
  47. }
  48. @Override
  49. public void onClick(View v) {
  50. switch (v.getId()) {
  51. case R.id.bindService:
  52. bindService();
  53. break;
  54. default:
  55. break;
  56. }
  57. }
  58. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/447918
推荐阅读
相关标签
  

闽ICP备14008679号