天天看點

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