天天看點

Android 完整項目打包成 aar 詳解

原創位址:https://blog.csdn.net/xiexiangyu92/article/details/75200091?utm_source=blogxgwz0支援原創 項目背景,公司要将完整APP打包成AAR包,供其它廠商内嵌。外部廠商提供殼工程和相應的Application調用我們提供的aar包

一路走來踩了一堆坑。。。

這裡先簡要解釋一下相關概念

1 什麼是AAR包? AAR包相比于jar包,差別在哪兒?

aar包含所有資源,class,xml布局檔案以及res資源檔案全部包含。注意是全部。

jar隻包含了class檔案與清單檔案,不包含資源檔案,如圖檔等所有res中的檔案。

捎帶解釋一下so庫,

2 什麼是so庫?什麼是ABI?相關的處理器型号在建構APP時有什麼差別?

android系統目前支援以下七種不同的CPU架構:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯着一個相應的ABI。

應用程式二進制接口ABI(Application Binary Interface)定義了二進制檔案(尤其是.so檔案)如何運作在相應的系統平台上,從使用的指令集,記憶體對齊到可用的系統函數庫。

so庫的好處:

  • so機制讓開發者最大化利用已有的C和C++代碼,達到重用的效果,利用軟體世界積累了幾十年的優秀代碼;
  • so是二進制,沒有解釋編譯的開消,用so實作的功能比純java實作的功能要快;
  • so記憶體配置設定不受Dalivik/ART的單個應用限制,減少OOM;
  • 相對于java代碼,二進制代碼的反編譯難度更大,一些核心代碼可以考慮放在so中。

在Android Studio建構APP時可以選擇建構時比對的CPU架構。在project的build.gradle可以明确指定,代碼如下

在buildType标簽下聲明

ndk{

abiFilters "armeabi","armeabi-v7a","x86"

}

以上代碼可以指定在建構時,生成支援這三類CPU的so庫。

so庫的load:

 1:相對路徑load: System.loadLibrary("media_jni"); 其中media_jni名字會被自動替換成libmedia_jni.so

在使用相對路徑load時,需要注意相應的so庫是否被打入到 aar包的libs目錄下。此處需要注意ABI類型

2: 絕對路徑load:System.load("/絕對路徑/libmedia_jni.so");

絕對路徑可以避免這個問題,但是要確定具有相應路徑的通路權限,在接入AAR時候,假設合作方是廠商ROM級别的,部分路徑需要提前協調。

jni層的方法對應關系:

全路徑,将.置換為_ 例如,假設目前函數native_init函數位于android.media這個包中,它的全路徑名應該是android.media.MediaScanner.native_init,而JNI層函數的名字是android_media_MediaScanner_native_init。

完整項目改造生成aar包的過程:

1 将原先module下的Application改為Library,正常調用assembleDebug 或 assembleRelease時就會在該module下的build/output目錄下生成aar檔案。

1.1 項目代碼中的switch語句需要改為if語句

1.2 修改Manifest.xml檔案

2 原有項目所依賴的jar包會被正常打包進aar中,但原項目依賴的aar則不會打包進aar

2.1 以外部compile形式所依賴的包,也不會被打包進aar

2.2 記得不要重複引用,避免殼工程引用的jar與打包好的aar沖突

3 聲明具體支援的so庫類型

3.1 最好在建構過程中聲明所支援的CPU類型。

Android系統的比對過程為從高到低,向下相容,例如:armeabi-v7a類型的CPU支援armeabi

3.2 如果不在BuildType中聲明,則預設支援所有類型的so庫檔案,通過反編譯在aar中的lib目錄下可以看到所支援的SO庫類型

3.3 部分so庫在不聲明的情況下,預設打在armeabi下,這樣會導緻armeabi-v7a類型的包找不到相應的so庫檔案。解決辦法就是強制聲明為armeabi類型

3.4 注意so庫存放的路徑

3.5 so庫本身是含有包名的,在jni使用的時候,需要将so庫方法的名稱,與調用so庫的代碼包名一緻

3.6 外部調用aar的殼工程,一般來說,會從aar中使用DexClassLoader,拷貝aar中的so庫到相應的目錄中。可以使用adb shell 到殼工程指定目錄下檢視是否so庫成功拷貝

3.7 so庫可以存放在aar的jniLibs下,也可以存放在殼工程的jniLibs和libs下。

3.8 遇到一次so庫崩潰,資訊與下段資訊類似:

http://blog.csdn.net/tankai19880619/article/details/9004619

在其中有相應的解決辦法,此處感謝部落客,運用文中方法,定位了問題。

4 Application參數的傳遞問題。

每一個Android App都有一個application context,這個參數需要殼工程傳遞給我們,調試的時候可以在殼工程的Manifest.xml中指定預設的Application.

并在預設的Application中初始化aar,

5 混淆

aar包也可以指定混淆方式,在提供給對方時,我們需要将代碼混淆,在廠商釋出時,也需要混淆,這樣就存在二次混淆後,AAR包找不到相應類的問題。

解決方法可以讓廠商在二次混淆時,keep住我們aar相關代碼

6 路徑擷取

留意相關路徑的擷取,在普通的Appliaction中,資料預設存儲在 /data/data/packageName/中,當aar内嵌在其他應用中,存儲路徑跟随主工程,在擷取資料時路徑切記不能寫死。

7 Assert資源擷取

因為是内嵌在其他應用中,原本APK中的Assert目錄内的資源可能找不到。解決方法是,可以将Assert目錄内的資源打包成一個ZIP檔案放在 /RAW目錄下。 在項目初始化的時候,解壓到指定目錄下。

使用的時候在AssertManager中使用絕對路徑擷取資源 在于前端頁面互動上,JS相關代碼及資源一般也存放在Assert目錄下,在webview.load()時,可以通過 file:/ 絕對路徑 ,來加載

PS:Assert 目錄與 /raw 目錄的差別:通路方式,目錄結構,大小寫,壓縮方式等。

8 第三方登入

因為微信微網誌等三方登入需要使用PackageName申請APPID,APPKEY, 需要使用殼工程的packageName。

9 常見BUG

9.1 java.lang.NoClassDefFoundError

NoClassDefFoundError錯誤的發生,是因為Java虛拟機在編譯時能找到合适的類,而在運作時不能找到合适的類導緻的錯誤。例如在運作時我們想調用某個類的方法或者通路這個類的靜态成員的時候,發現這個類不可用,此時Java虛拟機就會抛出NoClassDefFoundError錯誤。與ClassNotFoundException的不同在于,這個錯誤發生隻在運作時需要加載對應的類不成功,而不是編譯時發生。

9.2 jar包引用重複

9.3 contentProvider在注冊時出現重名情況

9.4 注意不要在殼工程的Activity中傳遞Context,可能出現Context為NULL的情況,最好在 殼工程的Application來初始化Context

9.5 java.lang.ExceptionInInitializerError 

原因如果你在别的類調用getInstance,就會報錯ExceptionInInitializerError。這是因為類加載時不會為執行個體變量指派,對象建立時不會為靜态變量指派。我們調用getInstance時,此類就開始加載,加載的時候不會為執行個體變量指派,但是會按順序給靜态變量指派。需要檢查變量初始化過程。

9.6 系統切換廣播監聽

在系統配置改變時,例如橫豎屏切換,會導緻Activity生命周期的改變。在開發過程中碰到一個問題,在使用者将應用點選home鍵置于背景的情況下切換語言,會導緻原有的注冊的receiver報錯。

排查後發現,在語言切換且應用存活的情況下,并不會走到應用的onDestory方法,而是重新走一次onCreate。

這就導緻了注冊兩次而報錯。