当前位置:   article > 正文

android多个app跨进程通信(IPC)实现(二)_android 跨app通讯

android 跨app通讯

android多个app跨进程通信(IPC)实现(一)

在文章一中实现了一个简单的字符串传递,对不知道怎么使用aidl的可以先去看看实现一,接下来会讲到对象和超过1M的数据怎么传递,

AIDL的传输数据机制基于BinderBinder对传输数据大小有限制, 传输超过1M的文件就会报android.os.TransactionTooLargeException异常,接下来看看使用匿名共享内存怎么进行大文件传输。

共享内存是进程间通信的一种方式,通过映射一块公共内存到各自的进程空间来达到共享内存的目的。

对于进程间需要传递大量数据的场景下,这种通信方式是十分高效的,但是共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。

Android中的匿名共享内存(Ashmem)是基于Linux共享内存的,借助Binder+文件描述符(FileDescriptor)实现了共享内存的传递。它可以让多个进程操作同一块内存区域,并且除了物理内存限制,没有其他大小限制。相对于Linux的共享内存,Ashmem对内存的管理更加精细化,并且添加了互斥锁。Java层在使用时需要用到MemoryFile,它封装了native代码。Android平台上共享内存通常的做法如下:

  1. 进程A通过MemoryFile创建共享内存,得到fd(FileDescriptor)
  2. 进程A通过fd将数据写入共享内存
  3. 进程A将fd封装成实现Parcelable接口的ParcelFileDescriptor对象,通过Binder将ParcelFileDescriptor对象发送给进程B
  4. 进程B获从ParcelFileDescriptor对象中获取fd,从fd中读取数据

概念引用其他地方的,重点是接下来的步骤,项目沿用实现一的工程,大体创建文件的步骤类似,创建2个新的module,bigObjectClientApp和bigObjectServerApp。

创建IMyAidlInterface.aidl

  1. interface IMyAidlInterface {
  2. void clientSendserver(in ParcelFileDescriptor pfd);
  3. }

服务端:

1、创建BigIpcService,实现IMyAidlInterface接口,接收从客户端发来的数据

  1. private val myBinder = object : IMyAidlInterface.Stub() {
  2. //接收客户端发来的数据
  3. override fun clientSendserver(pfd: ParcelFileDescriptor?) {
  4. /*** 从ParcelFileDescriptor中获取FileDescriptor */
  5. val fileDescriptor = pfd?.fileDescriptor
  6. /*** 根据FileDescriptor构建InputStream对象 */
  7. val fis = FileInputStream(fileDescriptor)
  8. /*** 从InputStream中读取字节数组 */
  9. val data = fis.readBytes()
  10. ipcBitmapBinder(data)
  11. }
  12. }
  13. private fun ipcBitmapBinder(byteArray: ByteArray) {
  14. val intent = Intent(this, MainActivity::class.java)
  15. val bundle = Bundle()
  16. bundle.putBinder("bitmap", ImageBinder(byteArray))
  17. intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
  18. intent.putExtras(bundle)
  19. startActivity(intent)
  20. }

在这里用到了另外一种突破binder只能传递小于1M数据的方法,使用putBinder

客户端:

2、首先bindService,参考前面代码,客户端向服务端发送数据

  1. private fun sendBigImage() {
  2. ipcAidl?.apply {
  3. try {
  4. /*** 读取assets目录下文件 */
  5. val inputStream = assets.open("client.jpg")
  6. /*** 将inputStream转换成字节数组 */
  7. val byteArray = inputStream.readBytes()
  8. /*** 创建MemoryFile */
  9. val memoryFile = MemoryFile("image", byteArray.size)
  10. /*** 向MemoryFile中写入字节数组 */
  11. memoryFile.writeBytes(byteArray, 0, 0, byteArray.size)
  12. /**
  13. * 获取MemoryFile对应的FileDescriptor
  14. * MemoryFile下的getFileDescriptor是@hide,在这用反射使用他
  15. */
  16. val fd = getFileDescriptor(memoryFile)
  17. /*** 根据FileDescriptor创建ParcelFileDescriptor */
  18. val pfd = ParcelFileDescriptor.dup(fd)
  19. /*** 发送数据 */
  20. ipcAidl?.clientSendserver(pfd)
  21. } catch (e: IOException) {
  22. e.printStackTrace()
  23. } catch (e: RemoteException) {
  24. e.printStackTrace()
  25. }
  26. }
  27. }

读取图片client.jpg,大小有4M多,这里主要注意getFileDescriptor方法,因为他属于hide的,只能通过反射使用,对应如下:

  1. private fun getFileDescriptor(memoryFile: MemoryFile?): FileDescriptor? {
  2. if (memoryFile == null)
  3. return null
  4. //val fd: FileDescriptor?
  5. return invokeKt(
  6. "android.os.MemoryFile",
  7. memoryFile,
  8. "getFileDescriptor"
  9. ) as FileDescriptor
  10. }
  11. private fun invokeKt(
  12. className: String,
  13. instance: Any?,
  14. methodName: String,
  15. ): Any? {
  16. try {
  17. val c = Class.forName(className)
  18. val method: Method = c.getDeclaredMethod(methodName)
  19. method.isAccessible = true
  20. return method.invoke(instance)
  21. } catch (e: java.lang.Exception) {
  22. e.printStackTrace()
  23. }
  24. return null
  25. }

自此客户端向服务端发送大图片的流程到底结果,欣赏下效果:

 服务端向客户端发送,主要采用回调方式

服务端:

1、首先在server端创建ICallbackInterface.aidl

  1. interface ICallbackInterface {
  2. void serverSendclient(in ParcelFileDescriptor pfd);
  3. }

2、在IMyAidlInterface添加注册和取消注册的回调方法

  1. import com.ninjuli.bigobject.ipc.ICallbackInterface;
  2. interface IMyAidlInterface {
  3. void clientSendserver(in ParcelFileDescriptor pfd);
  4. void registerCallback(ICallbackInterface callback);
  5. void unregisterCallback(ICallbackInterface callback);
  6. }

注意需要导入回调接口的路径

3、在BigIpcService实现添加的回调方法

  1. private val myBinder = object : IMyAidlInterface.Stub() {
  2. //接收客户端发来的数据
  3. override fun clientSendserver(pfd: ParcelFileDescriptor?) {
  4. ……
  5. }
  6. override fun registerCallback(callback: ICallbackInterface?) {
  7. callBackList.register(callback)
  8. }
  9. override fun unregisterCallback(callback: ICallbackInterface?) {
  10. callBackList.unregister(callback)
  11. }
  12. }

4、发送数据到客户端

  1. private fun serverToClient(pfd: () -> ParcelFileDescriptor?) {
  2. val n = callBackList.beginBroadcast()
  3. for (i in 0 until n) {
  4. val callback = callBackList.getBroadcastItem(i);
  5. if (callback != null) {
  6. try {
  7. callback.serverSendclient(pfd())
  8. } catch (e: RemoteException) {
  9. e.printStackTrace()
  10. }
  11. }
  12. }
  13. callBackList.finishBroadcast()
  14. }

客户端:

和实现一类似,只是多了一个注册回调的监听registerCallback(serverCallBack)

  1. private val serviceConnection = object : ServiceConnection {
  2. override fun onServiceConnected(p0: ComponentName?, iBinder: IBinder?) {
  3. ipcAidl = IMyAidlInterface.Stub.asInterface(iBinder)
  4. ipcAidl?.registerCallback(serverCallBack)
  5. }
  6. override fun onServiceDisconnected(p0: ComponentName?) {
  7. ipcAidl = null
  8. }
  9. }
  10. private val serverCallBack = object : ICallbackInterface.Stub() {
  11. override fun serverSendclient(pfd: ParcelFileDescriptor?) {
  12. val fileDescriptor = pfd?.fileDescriptor
  13. val fis = FileInputStream(fileDescriptor)
  14. val bytes = fis.readBytes()
  15. bytes.let {
  16. val bitMap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
  17. runOnUiThread {
  18. findViewById<ImageView>(R.id.image).setImageBitmap(bitMap)
  19. }
  20. }
  21. }
  22. }

项目地址:https://github.com/ninjuli/MultipleIPC

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/122649
推荐阅读
相关标签
  

闽ICP备14008679号