天天看點

android元件化打包module遇到的問題總結(打包成aar)

在一開始接觸APICLoud平台的時候我是拒絕的,因為對于一個有着熟練的java技能,掌握着老舊設計模式的人來說,這種平台簡直就是對于程式員這個職業的侮辱。第一個原因是APICLoud平台剝離了原生開發和html+js開發的職責,使得原生開發的職業方向越來越窄,開發中的地位也逐漸下降;第二個原因是,這個平台完全是無腦式開發,所有的js架構都是封裝好的,隻需要像jQuery一樣直接調用就好了,APICLoud架構的設計使得前端開發者開發起來特别簡單,前端如果轉apicloud開發成本會很低,完全用不着設計模式。

現在想想之前的想法過于偏激,社會就是越來越現代化,開發語言隻是一種工具,人都說君欲善其事,必先利器。我覺得很有道理,這些平台的發展就是為了減少開發人員的代碼編寫工作,集中注意力在業務邏輯上,也可以使得企業能夠快速疊代産品,進而減少運作成本,安卓/ios開發一直都飽受着混合開發和RN開發的折磨,現在這個APICLoud平台的出現我覺得是混合開發平台成熟的标志,因為該平台不僅僅是混合開發的架構,而且包含着混合開發的所有安卓/ios的第三方sdk的商店,也就是api服務商業化模式,這種模式對于開發人員的沖擊是相當大的,當sdk商店中已經集齊了所有滿足日常的sdk的時候,原生人員也就失業了,因為所有的基礎功能商店已經有,前端隻需要配置進apicloud架構就可以了,企業的開發人員根本不需要重複造輪子,可以把大量的時間放在業務邏輯上。

在學習安卓原生的過程中,讓我明白了很多道理,那就是這個世界唯一不變的是改變,唯有不斷的改變和學習才能跟的上時代的步伐,授人以魚不如授人以漁,學習過程中也積累了很多學習經驗,學習效率明顯得到了提高,現在我馬上要轉到小程式和網頁編寫了,想想也有點小激動呢!經過一個禮拜的學習,基本上可以做一些頁面了,路漫漫其修遠兮,吾将上下而求索。

在自定義子產品的時候出現的問題

APICLoud的平台自定義子產品上傳需要的是aar包,而aar包的特點就是内部會打包jar和jni裡面的so檔案,但是不會把gradle的遠端依賴下載下傳并且打包進aar檔案中,這就需要手動下載下傳complie所引用的遠端庫了。

什麼是gradle?##

  • Gradle是一個基于Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基于Groovy的特定領域語言(DSL)來聲明項目設定,抛棄了基于XML的各種繁瑣配置。面向Java應用為主。目前其支援的語言限于Java、Groovy、Kotlin和Scala。
               

    他的功能是根據編寫的gradle task來完成項目建構任務的,我們android studio中的gradle檔案就是用來編譯我們的安卓項目的,其中包含插件導入,利用插件輔助編譯,多個編譯檔案合并操作,引入遠端倉庫等等,gradle的功能非常強大。

    gradle官網可以檢視用法

利用gradle來完成打包任務

1、下載下傳遠端庫

  • 電腦上安裝一個gradle軟體,配置好環境變量。

    接下來就可以進行下載下傳任務了。

    參考:點選這裡

    使用此方法下載下傳Jar包的前提是已經配置好了Gradle的環境了,配置好的标志是在終端輸入gradle不提示command not found。

1. 編寫build.gradle檔案代碼:
apply plugin: 'java'
repositories {
    mavenCentral()
    jcenter()
    google()
}
dependencies {
    compile 'io.reactivex.rxjava2:rxjava:2.1.3'
}

task copyJars(type: Copy) {
  from configurations.runtime
  into 'lib' // 目标位置
}

2. 執行下載下傳的指令
gradle copyJars
           

如果上述代碼運作之後,發現不能下載下傳成功,那麼更換倉庫位址,使用國内鏡像下載下傳:

maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }  //阿裡鏡像
        maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}  //阿裡鏡像
        mavenCentral()
        jcenter()
        google()
           

2、合并依賴包

  • 1> libs下面的包合并

    一般我都是用代碼編寫一個task,把所有dependencies{}括号包含的語句拆解成一句一句,因為可能引用裡面版本不一緻,就會導緻下載下傳下來的重名的jar或者aar會互相覆寫,這個需要自己手動去排查版本,比如說support包需要版本一緻,而okhttp和io-socket都引用了oki.jar包,就是用代碼讓每一個依賴下載下傳下來的檔案都放在單獨的一個檔案夾,然後自己手動去合并這些包到libs中,這裡一定要細心和耐心,也要做到版本就低不就高。

    如果出現什麼mutilclass , duplicate resource等等都是重複引用的導緻的問題。

  • 2> so檔案的合并問題

    so檔案也需要手動去合并,因為你自定義的module可能會和其他的module的so檔案沖突,這個就需要在android{}标簽内加入

packagingOptions {
        //jni包重複編譯,那麼在這裡配置包包含 或者不包含
        exclude 'lib/mips/librsjni.so'
        exclude 'lib/armeabi-v7a/libRSSupport.so'
        exclude 'lib/arm64-v8a/librsjni.so'
        exclude 'lib/arm64-v8a/libRSSupport.so'
        exclude 'lib/x86_64/libRSSupport.so'
        exclude 'lib/mips/libRSSupport.so'
        exclude 'lib/x86/librsjni.so'
        exclude 'lib/x86_64/librsjni.so'
        exclude 'lib/armeabi-v7a/librsjni.so'
        exclude 'lib/x86/libRSSupport.so'
    }
           

還要一個就是其他module有的相容版本,你沒有,那麼就需要手動複制相容版本檔案夾,把對應的檔案導入,比如,APICLOUD平台隻有

android元件化打包module遇到的問題總結(打包成aar)

而你的module有x86 armeabi64 armeabi和armeabi-v7a cpu版本相容,那麼在編譯的時候就會包含四個檔案夾,而其他module隻有兩個,那麼就會運作時就會缺包,如果是這種情況,那麼就把自己的包x86 armeabi64 删掉,或者在 defaultConfig{}标簽内加入

ndk {
            abiFilters  "armeabi-v7a", "armeabi"
        }
           

這樣編譯的時候就隻會保留 “armeabi-v7a”, "armeabi"檔案夾。

關于so檔案相容請看:

連結内容

  • 3> 插件導入

    插件是gradle運作的環境,前提條件,比如說

apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
           

這裡的意思就是說目前gradle的任務是把這個module當成是library來編譯,在運作gradle之前加載butterknife插件,至于如何配置butterknife的話,這裡不細說,但是要把module打包成aar的話,加載butterknife插件必須本地加載,因為aar不會自動下載下傳遠端依賴的,同理使用第一步所說的下載下傳butterknife所需要的jar包将它們放入lib(注意是建立的)檔案夾。然後在gradle的根部添加如下代碼:

//展開libs檔案夾裡面的檔案,aar檔案依賴必須的配置
repositories{
    flatDir{
        dirs 'libs'
    }
}
/* buildscript這是gradle環境本身所依賴的庫,一般是連結
maven和git,Jcenter的倉庫加載插件的,但是這裡必須本地加載,
下面是把所有的lib檔案中的檔案加載進去*/
buildscript {
    dependencies {
        classpath fileTree(include: ['*.jar'], dir: 'lib')
    }
}

           

2、項目合并

  • 上面隻是僅僅把module中所有的jar和aar都合并進去了,接下來還要考慮,不同的module直接依賴沖突的問題,先來了解下gradle依賴的幾個關鍵字:
Android Studio引用第三方庫很友善,隻需要一句代碼就可以搞定,幾種引用第三方庫的方式,總結一下:

方式:1:它就會自動把這個包下載下傳下來,并且引用它。節省git空間,而且修改版本也很友善。
compile 'com.android.support:support-v4:23.3.0'

方式2:引用libs下所有jar包
compile fileTree(dir: 'libs', include: ['*.jar'])

方式3:引用一個jar
compile files('libs/fastjson-1.1.53.android.jar')

方式4:引用一個aar檔案,注意并不能像 方式2 那樣自動引用全部的aar,而需要對每個aar分别進行引用。
compile(name: 'aar_file_name', ext: 'aar')

方式5:引用庫類型的項目
compile project(':xxxsdk')

方式6:僅僅在編譯時使用,但最終不會被編譯到apk或aar裡
provided files('libs/glide-3.7.0.jar')
           
  • 經過本人實驗,貌似是資源檔案在打包編譯的時候如果重名就會覆寫,而class檔案重名就是直接編譯報錯,是以資源檔案沖突是隐形的,class沖突比較明顯。

    jar沖突的話,讓主module依賴這個jar,其他次module使用

    provided

    關鍵字依賴這個jar包,這樣編譯的時候就隻有一份。

    aar沖突比較麻煩了,因為aar中可能會包含jar檔案,這樣的話就需要檢視官方文檔或者自己解壓縮檔案去檢視這個aar裡面究竟包含着那些jar檔案,利用

    exclude group:"", module:""

    語句來排除jar被編譯進aar包中。例如:
    android元件化打包module遇到的問題總結(打包成aar)
  • 最後如果是資源檔案沖突,最常見的的錯誤就是:
no resource id found
           

或者是

java.lang.NoSuchFieldError: No static field xxx of 
type I in class Lcom/XX/R$id; or its superclasses
           

要麼就是多個module的資源檔案重名,導緻互相覆寫,那麼就找不到指定的資源,要麼就是指定的控件ID重名,多個重名控件導緻找不到控件,解決辦法也很簡單就是改檔案或者控件名字。

比如說我在APICLOUD雲平台編譯的時候出現以下錯誤,

What went wrong: 
 Execution failed for task ':app:transformClassesWithJarMergingForRelease'
 .> com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: android/support/v4/app/BaseFragmentActivityDonut.class
           

意思就是class檔案重複,原因就是apicloud的架構本身自帶v4包,而我的module也有v4包,在引入我的自定義子產品後,打包的時候就會出現相同的兩個class檔案,這樣就出現了以上錯誤,解決的辦法,上面已經說了使用exclude文法,将相應的部分排除,這個方法隻能排除網絡庫,本地的還沒發現能夠有效起到作用,是以,我在想,既然apicloud架構本身自帶v4的class檔案,而我自定義子產品打包成aar檔案又不會把v4.aar打包進去,還是要手動将v4.aar放到和module.aar同目錄下,那麼,我可以不将v4.aar放入,而是使用apicloud架構自帶的v4.aar,這樣就巧妙的将重複問題解決了。記錄下這個問題。

新版gradle的新文法:

Gradle 3.4 引入了新的依賴配置,

新增

api 和 implementation 來代替 compile 依賴配置。

其中 api 和以前的 compile 依賴配置是一樣的。

使用 implementation 依賴配置,會顯著提升建構時間,因為implementation隻會依賴直接引用的資源,而資源本身所依賴的資源是不會去深度依賴,這樣就減少了,依賴編譯所消耗的時間。

比如A依賴B ,B依賴C 。A implementation B,隻會将B加入編譯。而 A api B或者A compile B就不僅僅隻編譯B也會将C加入編譯。

是以在使用implementation 依賴的時候,不要驚訝找不到類,找不到資源之類的事情,因為他的原理就是不深度依賴。要麼使用api(在高版本建議使用這個)和complie(低版本)。要麼自己手動在A中将所需要的資源都依賴過來。

implementation和api的差別還有就是在,moduleB中如果使用implementation來引用網絡或者本地資源,moduleA 依賴 moduleB這樣寫的話,還是找不到,如果moduleB api來依賴網絡或者本地資源,那麼這些資源就可以被moduleA找到,并使用。

新增

compileOnly provided gradle 添加依賴到編譯路徑,編譯時使用。(不會打包到APK)

runtimeOnly apk gradle 添加依賴隻打包到 APK,運作時使用。(不會添加到編譯路徑)

現在外國伺服器相當不穩定,經常性的連接配接不上他們的中央倉庫,是以自己收藏了幾個國内的鏡像,速度非常快。

maven { url ‘http://maven.aliyun.com/nexus/content/groups/public/’ }

maven{ url ‘http://maven.aliyun.com/nexus/content/repositories/jcenter’}

經常會出現,merge task失敗的問題 ,Task [App] not found in root project [project_name] 這種問題,一般可以去查找aar資源包内是否有重複的jar依賴,導緻合并編譯出來的jar失敗,但是aar重複并不會導緻這個問題,不知道谷歌是哪根經出了問題。