Android 实现 IPC 的几种方式:Messenger、匿
Android跨进程(跨APP)调度有多种方式,四大组件都是支持跨进程角度。
IPC方式
Activity
我们可以通过startActivity打开其他进程的页面,并且带上一些参数,数据大小限制在1MB(1020KB)
BroadcastReceiver
广播不仅可以在APP内部实现通信,也可以实现跨跨进程调度,系统提供了很多基于广播的接口,比如实现电话监听、WIFI状态等。
Service
可以通过Service和AIDL实现IPC,这种方式是APP内部跨进程最常用的方式。
ContentProvider
ContentProvider 也是很常见的IPC方式,比如系统提供的手机通讯录就是基于ContentProvider实现的。ContentProvider是基于数据库增删改查的思路来提供服务的。
Socket
通过四大组件可以实现IPC,另辟途径,我们还可以利用网络的特性,使用Socket实现IPC。
数据传输方式
通常实现IPC主要的功能就是消息传递和数据传输,实现数据传输的方式也是有多种。
Bundle
Activity、Service、Receiver都支持Intent中传递Bundle数据,由于Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间传输,可以传递基本类型、基本类型数组、序列化对象,但是大小是有限的,不能传递过大的文件,比如大图片。
如果要传递Bitmap,记得要对图片进行压缩,并且不要传PNG图片。
文件共享
也可以通过把数据写入文件中,比如SharedPreferences,然后通知其他进程读取数据,这种方式可以实现任意图片大小的传递,非常适合APP内部的多进程调度,如果是跨APP调度就要考虑到sdcard的存储权限问题了,所以这种方式的适用性不广。
Messenger
messenger来实现跨app通信也就是在两个不同的app中实现双向通信。其实Messenger底层也是使用aidl的方式来实现的,只不过其使用handler来处理消息。
教程
class MessengerService : Service() {
class MessengerHandler : Handler() {
override fun handleMessage(msg: Message?) {
// 来自客户端的消息
if (msg != null) {
Log.i("MessengerService", msg.data.getString("msg"))
}
}
}
private val messenger = Messenger(MessengerHandler())
override fun onBind(intent: Intent?): IBinder? {
return messenger.binder
}
}
在AndroidManifest.xml添加注册
<application>
<service android:name=".MessengerService" android:process=":messenger"/>
</application>
在Activity实现绑定服务
class MainActivity : AppCompatActivity() {
private val conn = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val messenger = Messenger(service)
// 发送消息
val message = Message()
message.data = Bundle()
message.data.putString("msg", "Wiki")
messenger.send(message)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(this, MessengerService::class.java), conn, Context.BIND_AUTO_CREATE)
}
}
AIDL+匿名共享内存(Ashmem)
如果我们需要通过IPC实现大文件传递,除了可以通过写入文件读取文件的方式实现,也可以同匿名内存的方式,这种方式可以实现不同的App之间传递大文件内容。
创建一个IMemoryAidlInterface.aidl文件,然后make Project 就会生成一个IMemoryAidlInterface.java文件
// IMemoryAidlInterface.aidl
package com.taoweiji.camerax.androidipcexample;
import android.os.ParcelFileDescriptor;
interface IMemoryAidlInterface {
ParcelFileDescriptor getParcelFileDescriptor();
}
创建服务
class MemoryFetchService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return MemoryFetchStub()
}
class MemoryFetchStub : IMemoryAidlInterface.Stub() {
override fun getParcelFileDescriptor(): ParcelFileDescriptor {
val data = "这里匿名共享内存".toByteArray()
val memoryFile = MemoryFile("test", data.size)
memoryFile.outputStream.write(data)
val method = memoryFile.javaClass.getDeclaredMethod("getFileDescriptor")
val fileDescriptor = method.invoke(memoryFile) as FileDescriptor
return ParcelFileDescriptor.dup(fileDescriptor)
}
}
}
Activity 通过AIDL读取MemoryFetchService写入匿名共享内存的信息
class MemoryExampleActivity : AppCompatActivity() {
private val conn = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val memoryAidlInterface = IMemoryAidlInterface.Stub.asInterface(service)
val fileDescriptor = memoryAidlInterface!!.parcelFileDescriptor.fileDescriptor
val fis = FileInputStream(fileDescriptor)
Log.i("匿名共享内存信息", String(fis.readBytes()))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(this, MemoryFetchService::class.java), conn, Context.BIND_AUTO_CREATE)
}
}
如果需要不不同的App之间使用AIDL,必须要复制生成后的.java文件到Activity所在的App,包名也要保持一致。