AndroidStudio下使用 AIDL 構(gòu)建跨進程 Service( 詳細代碼貼圖 ), 填補網(wǎng)上的大多數(shù)坑

現(xiàn)在百度一下 AIDL/跨進程 Service, 文章一大堆, 然而都是千篇一律, 存在很多同樣模棱兩可的坑, 而且沒有AndroidStudio的最終目錄樹, 做起來還是有各種各樣的不順.

先說一下幾個網(wǎng)上模棱兩可的問題:

  1. 客戶端和服務(wù)端不用必須兩個apk;
  2. AndroidManifest 聲明的 service 的 progress 不用必須寫 :remote, 這里是寫進程的名字, 可以寫任意字符;
  3. java.lang.SecurityException: Binder invocation to an incorrect interface 錯誤真不一定是因為客戶端和服務(wù)端的包名不一致導(dǎo)致的, 有可能是實例化AIDL接口的時候不是實現(xiàn)的 XXX.Stub

源碼放在了github: https://github.com/YouCii/LearnApp

下面說下基本的實現(xiàn)流程


AIDL最簡單實現(xiàn)流程

先寫服務(wù)端

  1. 新建AIDL文件

    新建完成后會在src/main下生成aidl目錄, 修改生成的aidl文件, 寫入自己的接口方法
  2. 編譯程序, AS 會在 build 目錄中自動生成 aidl 對應(yīng)的 java 實現(xiàn)
  3. 寫好遠程服務(wù)

    別忘了在 AndroidManifest中聲明

客戶端

  1. 把所有 aidl 文件及其包名全部復(fù)制到客戶端里, 要保證包名一致, 不過有人奇怪怎么能兩個apk同一個包名呢? 可以這樣做(這里的圖片使用了后面加入ServiceData/ISocketStateListener.aidl的情況, 請忽略這幾個文件)
  2. 實現(xiàn)客戶端執(zhí)行代碼, 這里簡化了無關(guān)代碼, 只需要 bindService 時 傳入創(chuàng)建的 connection, 獲取到 aidl 對應(yīng)的 java 對象即可

客戶端和服務(wù)端在一個apk里
網(wǎng)上都沒有提過這種情況, 其實是可以的, 根本不用拷貝aidl文件, 還要保證包名必須一致. 這種方式的唯一apk的結(jié)構(gòu)樹如下(這里的圖片使用了后面加入ServiceData/ISocketStateListener.aidl的情況, 請忽略這幾個文件)


更多的使用

AIDL默認(rèn)只能傳遞基本類型, 如果想傳遞自己的對象, 需要利用 Parcelable

如果想監(jiān)聽服務(wù)端, 需要再創(chuàng)建一個 aidl 接口

然后在服務(wù)端實現(xiàn)接口, 客戶端內(nèi)調(diào)用 aidl 對應(yīng)的 java 內(nèi)的方法即可
服務(wù)端(請忽略代碼里的錯誤, 這是為了演示修改出來的)

客戶端


碰見的各種坑

  • 報錯 Error:Execution failed for task ':app:compileDebugAidl'.


    原因是包名不匹配, 一定要注意aidl自己所在的包名, 以及引用的其他 aidl 所在的包名, 如果寫錯了as不會報錯, 編譯的時候才有問題, 一定要仔細檢查.

  • 自動生成的AIDL找不到Parcelable自定義對象問題, 原因在于 aidl 文件和 Parcelable對象的包名不一致, 一定要保證兩者所在的包名一模一樣

  • 報錯 java.lang.SecurityException: Binder invocation to an incorrect interface. 這里有兩種情況

    1. 客戶端和服務(wù)端的包名不一致導(dǎo)致, 如果是客戶端和服務(wù)端分開的實現(xiàn)形式, 建議直接復(fù)制服務(wù)端的 aidl 根目錄. 請參考上面的目錄樹;
    2. onBind中返回aidl對象return pitPatAidlStub; 或者 調(diào)用binder的設(shè)置接口方法aidlBinder.setSocketStateListener時, 錯誤的實例化了 new IPitPatAidlInterface()而不是 new IPitPatAidlInterface.Stub(), 實例化了new ISocketStateListener() 而不是 new ISocketStateListener.Stub()

其他說明

  • 其中還好奇試了下, 使用 啟動同一進程service的方式 啟動 聲明了progress的service, 結(jié)果報錯: proxy can`t cast to...的錯誤.
  • aidl接口傳參時寫的 in / out / inout 修飾符 也要知道, 否則會出現(xiàn)數(shù)據(jù)不同步的問題. in 代表客戶端傳入服務(wù)端, 如果在服務(wù)端修改 in 修飾的變量時, 客戶端的變量不會更改, 修改為 out 修飾時服務(wù)端的變動會同步給客戶端, 但是服務(wù)端拿到的對象內(nèi)的參數(shù)會是空的, 可以使用inout來同時滿足; 注意使用out或者inout修飾時, 自定義的pacelable對象不僅僅只實現(xiàn)writeToParcel, 還要手寫 fun readFromParcel(parcel: Parcel) 方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容