現(xiàn)在百度一下 AIDL/跨進程 Service, 文章一大堆, 然而都是千篇一律, 存在很多同樣模棱兩可的坑, 而且沒有AndroidStudio的最終目錄樹, 做起來還是有各種各樣的不順.
先說一下幾個網(wǎng)上模棱兩可的問題:
- 客戶端和服務(wù)端不用必須兩個apk;
- AndroidManifest 聲明的 service 的 progress 不用必須寫
:remote, 這里是寫進程的名字, 可以寫任意字符; - 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ù)端
-
新建AIDL文件新建完成后會在src/main下生成aidl目錄, 修改生成的aidl文件, 寫入自己的接口方法
-
編譯程序, AS 會在 build 目錄中自動生成 aidl 對應(yīng)的 java 實現(xiàn)
-
寫好遠程服務(wù)別忘了在 AndroidManifest中聲明
客戶端
- 把所有 aidl 文件及其包名全部復(fù)制到客戶端里, 要保證包名一致, 不過有人奇怪怎么能兩個apk同一個包名呢? 可以這樣做(這里的圖片使用了后面加入ServiceData/ISocketStateListener.aidl的情況, 請忽略這幾個文件)
-
實現(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. 這里有兩種情況
- 客戶端和服務(wù)端的包名不一致導(dǎo)致, 如果是客戶端和服務(wù)端分開的實現(xiàn)形式, 建議直接復(fù)制服務(wù)端的 aidl 根目錄. 請參考上面的目錄樹;
- 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)方法