文章目录
android跨进程(IPC)通信及AIDL
官方文档
- 官方文档详见
https://developer.android.google.cn/guide/components/aidl?hl=en
特别注意
-
aidl文件里不要出现中文,因为官方还不支持中文
-
aidl支持java基本数据类型,以及String,CharSequence,List,Map(其中List,Map的具体类分别是ArrayList,HashMap,Map不支持泛型)
-
aidl文件中引用的对象即使在同一目录,也要增加import语句
-
只能定义int和String常量,如const int VERSION=1;
-
传递对象时必须声明in out inout(默认是in),而基本类型默认是in也只能是in,一般情况都是采用in
-
对于声明out和inout的对象需要增加readFromParcel方法的实现
-
build.gradle需要把aidl目录也当作源码目录设置进去,否则会报“Unresolved reference:”,如
androd{ ... sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } } }
过程说明
- aidl文件看起来就像是一个接口类,跟我们写接口其实差不太多,如果我们在aidl里需要使用bean类,那么需要写一个aidl来声明bean类,类似这样
- 然后才可以在其他aidl中导入使用
- 写好aidl文件之后,我们需要build一下,这时会发现android sdk已经帮我们生成了一些代码
- 它是根据我们的aidl文件生成的接口类,里面有个抽象类叫Stub,它是继承android.os.Binder类,而Binder类实现IBinder接口,那么这个Stub跟我们有啥关系呢
- 我们已经在aidl定义了交互的接口,那么具体的实现呢,谁来负责,这个就得交给Stub了,因此我们一般都是继承Stub这个抽象类,把具体的接口实现了,这块是根据业务去做的,一般都是成为服务的一端去实现的,例如继承Service后在onBind里返回
- 服务端实现好了之后,接下来其实就是等客户端连过来然后去调用了
- 客户端一般都是通过bindService,然后在ServiceConnection里的建立连接成功的回调中得到了IBinder
- 那么客户端就可以根据sdk为我们生成好的接口(比如在本例中是IExample这个接口类)获得Stub这个抽象类,然后调用它的asInterface这个方法将IBinder传递给它,这样之后客户端就可以愉快地调用服务端的接口了
跨进程
- 为了跨进程,需要把service放置在另一进程中,需要在清单文件中声明android:process,其中冒号表示私有进程,后面的名字可以随便取,最后的进程名为"包名:自定义名"
<!-- 运行在私有进程--> <service android:name=".ConnectService" android:process=":remote123" />
标签 in out inout oneway
- in out inout都只能修饰参数,不能用在返回值,参数对象都需要实现Parcelable接口(暂没有迹象表明可以使用Serializable接口)
- oneway只在远程调用时起效,表示远程调用时不会阻塞调用者的线程,如果是本地调用则还是同步阻塞的
- in:客户端流向服务端,服务端所做的修改,客户端不会发送变化;一般参数传递都是采用in类型,双方互不影响
- out:服务端将会收到客户端对象,该对象不为null,但是它里面的字段为null,服务端所做的修改,客户端会同步变化; 对象需要有readFromParcel方法
- inout:服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动; 对象需要有readFromParcel方法
跨进程回调RemoteCallbackList
-
演示如何跨进程回调,主要是用到了android.os.RemoteCallbackList
var mListeners: RemoteCallbackList<IMessageListener> = RemoteCallbackList() val messageServiceBinder1 = object : IMessageService.Stub() { override fun unregisterMessageListener(listener: IMessageListener?) { showToast("unregister") //使用RemoteCallbackList才能使得远程调用的注册与反注册成功生效,因为跨了进程 mListeners.unregister(listener) } override fun registerMessageListener(listener: IMessageListener?) { showToast("register") mListeners.register(listener) } override fun sendMessage(msg: String?) { if (!mIsConneted) { return } var n= mListeners.beginBroadcast() for( i in 0..n-1){ //直接将客户端的信息返回 mListeners.getBroadcastItem(i).onMessage(msg) } mListeners.finishBroadcast() } }
多IBinder返回如何处理
- 假如需要返回多个IBinder对象,那么应该声明一个IBinder,这个IBinder用于返回其他IBinder
lateinit var serviceConnection: ServiceConnection var iConnectService: IConnectService? = null var iMessageService: IMessageService? = null var iServiceManager: IServiceManager? = null serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { Log.i(TAG, "onServiceConnected:" + name.flattenToShortString()) showToast("onServiceConnected:" + name.flattenToShortString()) iServiceManager = IServiceManager.Stub.asInterface(service) iConnectService = IConnectService.Stub.asInterface(iServiceManager?.connectServiceBinder) iMessageService = IMessageService.Stub.asInterface(iServiceManager?.messageServiceBinder) } override fun onServiceDisconnected(name: ComponentName) { Log.i(TAG, "onServiceDisconnected:" + name.flattenToShortString()) showToast("onServiceDisconnected:" + name.flattenToShortString()) iServiceManager = null iConnectService = null iMessageService = null } }
示例
- 本文示例已经放到github: https://github.com/android-coding-well/aidldemo
计划
- 接下来打算使用MemoryFile实现进程间共享内存
参考
-
官方说明
https://developer.android.google.cn/guide/components/aidl?hl=en -
AIDL中的in、out、inout的区别 - 简书
https://www.jianshu.com/p/a61da801b919 -
Android Service完全解析,关于服务你所需知道的一切(上) - 郭霖的专栏 - CSDN博客
https://blog.csdn.net/guolin_blog/article/details/11952435 -
Android Binder机制全面解析 - 简书
https://www.jianshu.com/p/b5cc1ef9f917 -
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制
http://www.360doc.com/content/19/0211/14/15700426_814240466.shtml