由于項目的特殊性挖藏,最近一直在往app工程里接入(更新)第三方sdk,并且app的一些基礎(chǔ)控件也被其他項目組封裝成了sdk來導(dǎo)入。在不停的sdk更新迭代過程中碰到了一些問題碎赢,記錄一下為后續(xù)排查問題依據(jù)荚虚。
sdk本身內(nèi)部可以依賴其他sdk庫(假設(shè)為庫A)薛夜,此時庫A不需要app顯示導(dǎo)入,它在導(dǎo)入sdk時會自動導(dǎo)入進來(sdk發(fā)布時的aar文件有一個配對的pom.xml文件版述,他可以用來描述依賴)却邓,但可以通過
exclude
關(guān)鍵字取消對庫A的自動導(dǎo)入。如果sdk本身依賴另一個庫A院水,且app本身也對A庫有導(dǎo)入依賴腊徙,這時就存在有兩個地方對同一個庫A的導(dǎo)入了。如果兩處的版本號不一致的話檬某,最終庫A的什么版本是哪個呢撬腾?是以app的導(dǎo)入為準(zhǔn)嗎?還是以版本高的為準(zhǔn)恢恼?結(jié)論是以版本號高的引用那個為準(zhǔn)(打印依賴有向圖可以看出)民傻。
問題:明明我只是升級了sdk的版本,但最終的依賴庫A的版本怎么也變了场斑?由于庫A的內(nèi)容變了漓踢,那調(diào)用庫A的代碼就有可能存在不一致性了,特別是涉及到大版本更新時漏隐。這是可以通過依賴關(guān)系圖來查看依賴的變化點和變化來源在哪里喧半。另外一種情況:還是以上面的結(jié)論為基礎(chǔ),假設(shè)app依賴的庫A版本比較高(最終以這個高版本為準(zhǔn))青责,且這個高版本庫A相比sdk依賴的低版本剛好刪掉某一個API接口(比如是大版本升級)挺据,而sdk內(nèi)部又剛好有調(diào)用這個刪掉了的接口。
問題:此時是app竟然可以編譯過2绷ァ1饽汀! 只是會等到在運行時拋出找不到方法的異常产阱。這怎么辦婉称?很嚴(yán)重,很隱秘的問題。
原因:針對這種情況剛開始還不能理解王暗,都少了一個調(diào)用方法了為啥編譯時沒報錯榨乎?后來想想也是正常的,這是因為app導(dǎo)入的sdk是以aar的形式引入的瘫筐,而這個aar在sdk發(fā)布時就已經(jīng)編譯成字節(jié)碼(class)了蜜暑,所以此時編譯app時編譯器并不會再用javac去編譯aar了(直接將aar的class類merge到app中),于是最終編譯器沒有檢查調(diào)用關(guān)系的錯誤策肝。
驗證:即使使用exclude
將依賴庫A排除依賴(最終的apk不包含庫A的代碼)也仍可以編譯過肛捍。
今天碰到了這種情況,一時無法理解:
首先是一個sdk依賴的庫A低版本(3.0.1)的接口:
//3.0.1
public static Context build(Activity activity, boolean param1, boolean param2) {
//...
}
然后是app依賴的庫A高版本(3.0.3)的接口:
//3.0.3
public static Context build(Context context, boolean param1, boolean param2) {
//...
}
3.0.1版本與3.0.3版本的差異只是build()函數(shù)的第一個參數(shù)Activity
變?yōu)榱?code>Context之众,但app升級了庫A3.0.3版本后編譯能通過拙毫,sdk內(nèi)部卻必現(xiàn)拋找不到方法異常:
java.lang.NoSuchMethodError: No static method build(Landroid/app/Activity;ZZ)Landroid/content/Context;
且我測試在app里調(diào)用這個build()方法又是正常的。為什么呢棺禾?原因就是上面說的(刪掉了方法與修改了方法簽名本質(zhì)上是一樣的)缀蹄,即使這里滿足多態(tài)性(Context
是Activity
的基類)也不行。
- 在編寫sdk時膘婶,暴露的API類和接口不宜輕易改動缺前,否則可能會出現(xiàn)上述的依賴問題。廢棄的方法和類可以使用注解
@Deprecated
標(biāo)識起來悬襟,需要改動的接口使用方法重載的方式增加接口衅码,而不是去改動接口。
查看app的依賴關(guān)系的命令:
./gradlew :app:dependencies