天天看点

android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL

文章目录

  • android跨进程(IPC)通信及AIDL
    • 官方文档
    • 特别注意
    • 过程说明
    • 跨进程
    • 标签 in out inout oneway
    • 跨进程回调RemoteCallbackList
    • 多IBinder返回如何处理
    • 示例
    • 计划
    • 参考

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类,类似这样
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • 然后才可以在其他aidl中导入使用
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • 写好aidl文件之后,我们需要build一下,这时会发现android sdk已经帮我们生成了一些代码
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • 它是根据我们的aidl文件生成的接口类,里面有个抽象类叫Stub,它是继承android.os.Binder类,而Binder类实现IBinder接口,那么这个Stub跟我们有啥关系呢
  • 我们已经在aidl定义了交互的接口,那么具体的实现呢,谁来负责,这个就得交给Stub了,因此我们一般都是继承Stub这个抽象类,把具体的接口实现了,这块是根据业务去做的,一般都是成为服务的一端去实现的,例如继承Service后在onBind里返回
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • 服务端实现好了之后,接下来其实就是等客户端连过来然后去调用了
  • 客户端一般都是通过bindService,然后在ServiceConnection里的建立连接成功的回调中得到了IBinder
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL
  • 那么客户端就可以根据sdk为我们生成好的接口(比如在本例中是IExample这个接口类)获得Stub这个抽象类,然后调用它的asInterface这个方法将IBinder传递给它,这样之后客户端就可以愉快地调用服务端的接口了
  • android跨进程(IPC)通信及AIDLandroid跨进程(IPC)通信及AIDL

跨进程

  • 为了跨进程,需要把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