現(xiàn)在百度一下 AIDL/跨進(jìn)程 Service, 文章一大堆, 然而都是千篇一律, 存在很多同樣模棱兩可的坑, 而且沒有AndroidStudio的最終目錄樹, 做起來(lái)還是有各種各樣的不順.
先說(shuō)一下幾個(gè)網(wǎng)上模棱兩可的問(wèn)題:
- 客戶端和服務(wù)端不用必須兩個(gè)apk;
- AndroidManifest 聲明的 service 的 progress 不用必須寫
:remote
, 這里是寫進(jìn)程的名字, 可以寫任意字符; - java.lang.SecurityException: Binder invocation to an incorrect interface 錯(cuò)誤真不一定是因?yàn)榭蛻舳撕头?wù)端的包名不一致導(dǎo)致的, 有可能是實(shí)例化AIDL接口的時(shí)候不是實(shí)現(xiàn)的
XXX.Stub
源碼放在了github: https://github.com/YouCii/LearnApp
下面說(shuō)下基本的實(shí)現(xiàn)流程
AIDL最簡(jiǎn)單實(shí)現(xiàn)流程
先寫服務(wù)端
-
新建AIDL文件
-
編譯程序, AS 會(huì)在 build 目錄中自動(dòng)生成 aidl 對(duì)應(yīng)的 java 實(shí)現(xiàn)
-
寫好遠(yuǎn)程服務(wù)
客戶端
- 把所有 aidl 文件及其包名全部復(fù)制到客戶端里, 要保證包名一致, 不過(guò)有人奇怪怎么能兩個(gè)apk同一個(gè)包名呢? 可以這樣做(這里的圖片使用了后面加入ServiceData/ISocketStateListener.aidl的情況, 請(qǐng)忽略這幾個(gè)文件)
-
實(shí)現(xiàn)客戶端執(zhí)行代碼, 這里簡(jiǎn)化了無(wú)關(guān)代碼, 只需要 bindService 時(shí) 傳入創(chuàng)建的 connection, 獲取到 aidl 對(duì)應(yīng)的 java 對(duì)象即可
客戶端和服務(wù)端在一個(gè)apk里
網(wǎng)上都沒有提過(guò)這種情況, 其實(shí)是可以的, 根本不用拷貝aidl文件, 還要保證包名必須一致. 這種方式的唯一apk的結(jié)構(gòu)樹如下(這里的圖片使用了后面加入ServiceData/ISocketStateListener.aidl的情況, 請(qǐng)忽略這幾個(gè)文件)
更多的使用
AIDL默認(rèn)只能傳遞基本類型, 如果想傳遞自己的對(duì)象, 需要利用 Parcelable
如果想監(jiān)聽服務(wù)端, 需要再創(chuàng)建一個(gè) aidl 接口
然后在服務(wù)端實(shí)現(xiàn)接口, 客戶端內(nèi)調(diào)用 aidl 對(duì)應(yīng)的 java 內(nèi)的方法即可
服務(wù)端(請(qǐng)忽略代碼里的錯(cuò)誤, 這是為了演示修改出來(lái)的)
碰見的各種坑
-
報(bào)錯(cuò) Error:Execution failed for task ':app:compileDebugAidl'.
原因是包名不匹配, 一定要注意aidl自己所在的包名, 以及引用的其他 aidl 所在的包名, 如果寫錯(cuò)了as不會(huì)報(bào)錯(cuò), 編譯的時(shí)候才有問(wèn)題, 一定要仔細(xì)檢查. -
自動(dòng)生成的AIDL找不到Parcelable自定義對(duì)象問(wèn)題, 原因在于 aidl 文件和 Parcelable對(duì)象的包名不一致, 一定要保證兩者所在的包名一模一樣
-
報(bào)錯(cuò) java.lang.SecurityException: Binder invocation to an incorrect interface. 這里有兩種情況
- 客戶端和服務(wù)端的包名不一致導(dǎo)致, 如果是客戶端和服務(wù)端分開的實(shí)現(xiàn)形式, 建議直接復(fù)制服務(wù)端的 aidl 根目錄. 請(qǐng)參考上面的目錄樹;
- onBind中返回aidl對(duì)象
return pitPatAidlStub;
或者 調(diào)用binder的設(shè)置接口方法aidlBinder.setSocketStateListener
時(shí), 錯(cuò)誤的實(shí)例化了new IPitPatAidlInterface()
而不是new IPitPatAidlInterface.Stub()
, 實(shí)例化了new ISocketStateListener()
而不是new ISocketStateListener.Stub()
其他說(shuō)明
- 其中還好奇試了下, 使用 啟動(dòng)同一進(jìn)程service的方式 啟動(dòng) 聲明了progress的service, 結(jié)果報(bào)錯(cuò): proxy can`t cast to...的錯(cuò)誤.
- aidl接口傳參時(shí)寫的 in / out / inout 修飾符 也要知道, 否則會(huì)出現(xiàn)數(shù)據(jù)不同步的問(wèn)題. in 代表客戶端傳入服務(wù)端, 如果在服務(wù)端修改 in 修飾的變量時(shí), 客戶端的變量不會(huì)更改, 修改為 out 修飾時(shí)服務(wù)端的變動(dòng)會(huì)同步給客戶端, 但是服務(wù)端拿到的對(duì)象內(nèi)的參數(shù)會(huì)是空的, 可以使用inout來(lái)同時(shí)滿足; 注意使用out或者inout修飾時(shí), 自定義的pacelable對(duì)象不僅僅只實(shí)現(xiàn)writeToParcel, 還要手寫
fun readFromParcel(parcel: Parcel)
方法