天天看點

在 Android N 如何編譯 android-support 等包并依賴到項目工程中

背景:

       之前在修改項目原生設定 Settings.apk 的問題時,曾經想修改 support-v7 包中的 RecyclerView.java 檔案,但是基于Android N 的環境下,修改後的代碼一直沒有編入到 Settings.apk。後來從另外的一個角度對問題進行了修改,避開了修改 android-support 包的雷區。最近在學習 support-v4 包中 ViewPager.java 的觸摸事件傳遞以及滑動沖突解決方案時,想着增加列印來增強對流程的了解,是以還是需要探究 android-support 的資源如何導入到項目工程。在此過程中遇到了一系列的問題,希望我的解決方案能夠幫助到大家。

結論:

  1. 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
               
  2. 修改完 Android.mk 檔案後,就可以直接進行編譯了,我是使用 mma 指令進行編譯,此時如無意外,可以看到編譯出了 javalib.jar。
  3. 此時的我們需要把該 jar 包導入到我們的項目工程中,此時分為2種情況:(1)如果我們是在 Android N 的源碼環境中編譯 apk,比如我們編譯 Hosting 項目的定制設定 Settings.apk,那麼直接進入到 Settings 的根目錄進行編譯即可,此時我使用的指令是 mm -B,如果能成功編譯出 apk, 那麼生成的修改後的 android-support 庫就會合入到 Settings.apk。(2)如果是使用 Android Studio 等IDE編譯第三方apk時,那麼需要手動将該 jar 包導入項目工程中,并且根據實際情況,需要解決資源沖突問題  

備注:

  1. 以上結論均為結果導向結論,即該文章提供了一種可行的解決方案,但對其原理的了解,有可能是錯誤的,各位看官需要自行判斷。
  2. 以上編譯方式究竟是使用“mma”,“mm -B”亦或其他指令,我沒有過多關注,我的需求是能編譯通過即可。
  3. 需要在 Android.mk 檔案中哪個(或者哪些) module 關閉 jack,這個也是結果導向的,我嘗試的情況是:隻需要在最終生成 jar 包的 module 處關閉 jack 即可。
  4. 如果 support-A.jar 需要依賴 support-B.jar,那麼 support-B.jar 需要不能關閉 jack,否則 support-A.jar 會編不出來。

下面講一下我處理這件事的整個流程:

  1. 之前修改 framework 的一些東西時,一般都是把一些庫檔案 push 到裝置中,比如 Android K 的 framework.jar、framework2.jar、ext.jar,Android N 的 boot.art 等檔案。修改 RecyclerView.java 時,按照正常思路,在源碼環境中編譯引用到 RecyclerView 的地方,應當能夠自動連結到修改後的代碼,但實際上是不行的(有可能有其他方案,可以讓項目工程直接連結到 jack 編譯的中間産物 classes.jack,不過目前沒有找到這類方案)。是以當時的想法是可能産生出了一些庫檔案,需要 push 到裝置中,但當時把所有能夠想到的庫檔案全部 push 到裝置,仍然沒有達到預期效果。
  2. 在 1 行不通的情況下,我對“項目工程運作時是調用裝置的 android-support 還是項目的 android-support”進行了分析,如果是前者,那麼 android-support 的 api 理應是通過 binder 機制進行動态連結,使用反編譯工具檢視 android -support.jar,發現沒有任何使用 binder 的痕迹,那麼說明 android-support 等庫是以靜态連結的方式導入到項目工程的,那麼接下來就需要将源代碼編譯為 jar 包,然後導入到項目工程中。
  3. Android N 采用 jcak 進行編譯,jack 編譯時,中間産物是 *.jack,不滿足導入項目工程的條件(可能存在向 Android Studio 連結 jack庫的方式,目前沒找到這種方案),是以我們可以暫時把該 module 的 jack 編譯給關閉了。此時編譯出 jar 包。
  4. 源碼環境下連結 jar 包的情況就不闡述了,由于大多數情況下需要在源碼環境編譯 apk 的情況,都不會産生依賴沖突的情況(需要導入的 android-support 等代碼源碼也有,沒有必要從外部導入,是以也不會出現版本沖突的問題),是以重點講一下在 Android Studio 等 IDE 中導入修改後的 android-support 是如何解決依賴沖突的問題的。
  5. Android Studio 有一個插件可以分析項目中是否存在依賴沖突(根據經驗法則,該工具未能保證每一個依賴沖突都被檢測出來,是以出現過檢測時沒問題,但是 build apk 還是報錯:資源沖突),如圖所示;如果配置了指令行工具,也可以使用指令行 gradle dependences 運作(我沒配,沒試過)
    在 Android N 如何編譯 android-support 等包并依賴到項目工程中
  6. 如果出現了沖突的資源,那麼隻需要在導入依賴時,把部分 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')
    }           
  7. 如上面代碼所示,我們在使用 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 掉。
    在 Android N 如何編譯 android-support 等包并依賴到項目工程中

備注:

       按照以上 7 個步驟,一般就能處理掉依賴沖突問題。下面再講一些我遇到的坑。

  1. 編譯出來的包在工程中存在 1 個即可,不同于使用 gradle 導入依賴,多個子產品可以導入 android-support,隻要保證版本一緻即可。如果一個工程中導入多個編譯後的 jar 包,那麼有可能出現資源沖突,并且主工程的 jar 包會顯示位于 android.jar 中。這個坑爹的形式導緻我花了不少時間在研究 sdk 中android.jar 跟 sdk 中 android-support 是如何連結在一起的。
  2. 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 個包,再按照上面的流程解決依賴沖突問題。