背景:
之前在修改項目原生設定 Settings.apk 的問題時,曾經想修改 support-v7 包中的 RecyclerView.java 檔案,但是基于Android N 的環境下,修改後的代碼一直沒有編入到 Settings.apk。後來從另外的一個角度對問題進行了修改,避開了修改 android-support 包的雷區。最近在學習 support-v4 包中 ViewPager.java 的觸摸事件傳遞以及滑動沖突解決方案時,想着增加列印來增強對流程的了解,是以還是需要探究 android-support 的資源如何導入到項目工程。在此過程中遇到了一系列的問題,希望我的解決方案能夠幫助到大家。
結論:
- Android N 環境使用 jack 編譯方式,是不能編譯出 jar 包的,這個直接導緻了修改 android-support 源碼後,修改後的代碼沒有編入項目工程。解決方案:我們修改對應 jar 包源碼的 Android.mk 檔案,臨時将 jack 的編譯方式關掉即可。
[email protected]:~/sdk/androidN/frameworks/support$ git diff v4/Android.mk diff --git a/v4/Android.mk b/v4/Android.mk index 1e8adad..248f5ab 100644 --- a/v4/Android.mk +++ b/v4/Android.mk @@ -264,6 +264,7 @@ support_module_src_files += $(LOCAL_SRC_FILES) # Here is the final static library that apps can link against. include $(CLEAR_VARS) # 關閉 jack 編譯 +LOCAL_JACK_ENABLED := disabled LOCAL_USE_AAPT2 := true # 在最終生成 jar 包的配置處關閉 jack 即可 LOCAL_MODULE := android-support-v4 LOCAL_SDK_VERSION := 4
- 修改完 Android.mk 檔案後,就可以直接進行編譯了,我是使用 mma 指令進行編譯,此時如無意外,可以看到編譯出了 javalib.jar。
- 此時的我們需要把該 jar 包導入到我們的項目工程中,此時分為2種情況:(1)如果我們是在 Android N 的源碼環境中編譯 apk,比如我們編譯 Hosting 項目的定制設定 Settings.apk,那麼直接進入到 Settings 的根目錄進行編譯即可,此時我使用的指令是 mm -B,如果能成功編譯出 apk, 那麼生成的修改後的 android-support 庫就會合入到 Settings.apk。(2)如果是使用 Android Studio 等IDE編譯第三方apk時,那麼需要手動将該 jar 包導入項目工程中,并且根據實際情況,需要解決資源沖突問題
備注:
- 以上結論均為結果導向結論,即該文章提供了一種可行的解決方案,但對其原理的了解,有可能是錯誤的,各位看官需要自行判斷。
- 以上編譯方式究竟是使用“mma”,“mm -B”亦或其他指令,我沒有過多關注,我的需求是能編譯通過即可。
- 需要在 Android.mk 檔案中哪個(或者哪些) module 關閉 jack,這個也是結果導向的,我嘗試的情況是:隻需要在最終生成 jar 包的 module 處關閉 jack 即可。
- 如果 support-A.jar 需要依賴 support-B.jar,那麼 support-B.jar 需要不能關閉 jack,否則 support-A.jar 會編不出來。
下面講一下我處理這件事的整個流程:
- 之前修改 framework 的一些東西時,一般都是把一些庫檔案 push 到裝置中,比如 Android K 的 framework.jar、framework2.jar、ext.jar,Android N 的 boot.art 等檔案。修改 RecyclerView.java 時,按照正常思路,在源碼環境中編譯引用到 RecyclerView 的地方,應當能夠自動連結到修改後的代碼,但實際上是不行的(有可能有其他方案,可以讓項目工程直接連結到 jack 編譯的中間産物 classes.jack,不過目前沒有找到這類方案)。是以當時的想法是可能産生出了一些庫檔案,需要 push 到裝置中,但當時把所有能夠想到的庫檔案全部 push 到裝置,仍然沒有達到預期效果。
- 在 1 行不通的情況下,我對“項目工程運作時是調用裝置的 android-support 還是項目的 android-support”進行了分析,如果是前者,那麼 android-support 的 api 理應是通過 binder 機制進行動态連結,使用反編譯工具檢視 android -support.jar,發現沒有任何使用 binder 的痕迹,那麼說明 android-support 等庫是以靜态連結的方式導入到項目工程的,那麼接下來就需要将源代碼編譯為 jar 包,然後導入到項目工程中。
- Android N 采用 jcak 進行編譯,jack 編譯時,中間産物是 *.jack,不滿足導入項目工程的條件(可能存在向 Android Studio 連結 jack庫的方式,目前沒找到這種方案),是以我們可以暫時把該 module 的 jack 編譯給關閉了。此時編譯出 jar 包。
- 源碼環境下連結 jar 包的情況就不闡述了,由于大多數情況下需要在源碼環境編譯 apk 的情況,都不會産生依賴沖突的情況(需要導入的 android-support 等代碼源碼也有,沒有必要從外部導入,是以也不會出現版本沖突的問題),是以重點講一下在 Android Studio 等 IDE 中導入修改後的 android-support 是如何解決依賴沖突的問題的。
- Android Studio 有一個插件可以分析項目中是否存在依賴沖突(根據經驗法則,該工具未能保證每一個依賴沖突都被檢測出來,是以出現過檢測時沒問題,但是 build apk 還是報錯:資源沖突),如圖所示;如果配置了指令行工具,也可以使用指令行 gradle dependences 運作(我沒配,沒試過)
- 如果出現了沖突的資源,那麼隻需要在導入依賴時,把部分 group 或者 module 給排除掉即可,大緻配置如下
# build.gradle # 集中解除依賴 android { ... configurations { all*.exclude group: 'org.hamcrest', module: 'hamcrest-core' } } # 分别解除依賴 dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.0.0',{ exclude module: 'support-v4' exclude module: 'support-annotations' } compile 'com.android.support:design:24.0.0',{ exclude module: 'support-v4' exclude module: 'support-annotations' } compile 'com.android.support:cardview-v7:24.0.0',{ exclude module: 'support-v4' exclude module: 'support-annotations' } compile 'com.android.support:recyclerview-v7:24.0.0',{ exclude module: 'support-v4' exclude module: 'support-annotations' } compile project(':viewPagerLibrary') }
- 如上面代碼所示,我們在使用 gradle 導入依賴時,一般都會這麼寫“compile 'com.android.support:appcompat-v7:24.0.0'”,其中“com.android.support”就是 group,而“appcompat-v7”就是 module,“24.0.0”就是 version,上面的代碼就是導入 group 為 com.android.support,module 為 appcompat-v7,版本為 24.0.0,但排除掉 module 為“support-v4”和“support-annotations”的子產品。那麼如何确定相對應的 group 跟 module 呢?一方面可以從 gradle dependences 的分析結果看出,另一方面在 build apk,如果報錯,那麼可以從沖突的類名,使用 double shift 快捷鍵檢視目前項目存在多少個同名檔案,根據右側的檔案來源可以确定哪些 module 發生了沖突。如下圖,本地 support-v4.jar 與 gradle 導入的 support-annotations 發生了沖突,那麼根據我的需求,我就需要把 support-annotations 給 exclude 掉。
備注:
按照以上 7 個步驟,一般就能處理掉依賴沖突問題。下面再講一些我遇到的坑。
- 編譯出來的包在工程中存在 1 個即可,不同于使用 gradle 導入依賴,多個子產品可以導入 android-support,隻要保證版本一緻即可。如果一個工程中導入多個編譯後的 jar 包,那麼有可能出現資源沖突,并且主工程的 jar 包會顯示位于 android.jar 中。這個坑爹的形式導緻我花了不少時間在研究 sdk 中android.jar 跟 sdk 中 android-support 是如何連結在一起的。
- android-support-v4 24.2.0 後被拆分為 5 個部分:support-compat、support-core-utils、support-core-ui、support-media-compat、support-fragment,由于我導入的包剛好是 24.2.0 的版本,一開始跟着網上的寫法“exclude module: 'support-v4'”并不能解決沖突問題,是以極度懷疑通過 exclude 這種方案依賴檔案重複的可行性,這後面也花了很多時間去調試,幸好最後百度到這篇文章。是以,解決這個問題,要麼把版本包從 24.2.0 降到 24.0.0或者更低版本,要麼修改源碼系統 Android N 的 android.mk 檔案,将其 v4 包分解為 5 個包,再按照上面的流程解決依賴沖突問題。