1. 程式人生 > >關於創建Android Library所須要知道的一切

關於創建Android Library所須要知道的一切

cati ati edit file pre IT onf rate one

關於創建Android Library所須要知道的一切

Android 庫(Library)在結構上與 Android 應用模塊同樣。應用模塊所能夠包括的東西。在庫中都同意存在,包括代碼文件、資源文件和manifest文件等。

應用模塊編譯後生成的是一個apk文件,能夠直接在設備上執行,可是,庫模塊編譯後生成的是一個Android Archive文件,簡稱AAR

AAR文件無法像apk文件一樣直接在設備上執行,我們一般用它作為Android app的依賴。

普通JAR文件僅僅能包括代碼文件和清單文件,而ARR文件不僅能夠包括代碼文件。還能夠包括Android的資源文件和manifest文件。這樣。我們就能夠把資源文件像布局文件、圖片文件等和Java代碼文件一起分享出去。能夠說ARR文件是真正專屬於Android的“JAR”包。

庫模塊在下面情況下非常實用:

  • 創建多個app。這些app須要使用多個同樣的組件。像activity、service或UI 布局等。
  • 創建一個app。而這個app可能須要依據須要編譯成多個APK版本號,比方免費版和付費版,而兩個版本號都須要使用到同樣的組件。

在不論什麽一種情況下,你僅僅須要將要重用的文件放到庫模塊中,然後以依賴項的形式為每一個應用模塊加入庫就可以。

創建庫模塊

在你的工程中,創建一個新的庫模塊。能夠遵循例如以下的步驟:

  1. 點擊 File > New > New Module.
  2. 在Create New Module的窗體中。選擇Android Library。並點擊下一步(Next)。
    在該窗體中還有一個選項用於創建一個Java Library,Java Library就是我們所知的傳統的JAR文件。JAR文件在非常多工程中十分實用,尤其當你想分享代碼給其它工程的時候。可是JAR文件並不同意包括Android資源文件和manifest文件,而資源文件在Android項目中對代碼重用具有非常大的幫助。所以本篇主要對Android庫作介紹。
  3. 為你的庫命名並選擇最低SDK版本號號,然後點擊Finish,完畢創建。

僅僅要Gradle同步完畢後,庫模塊就會出現左邊的工程面板中。

應用模塊轉成庫模塊

假設你有一個已經存在的應用模塊。並想重用它的全部代碼。你能夠把它轉成一個庫模塊:

1.打開屬於該應用模塊下的build.gradle文件,在最頂部,你能夠看見例如以下的顯示:

java apply plugin: ‘com.android.application‘ 

2.把應用的插件改成庫的插件:

java apply plugin: ‘com.android.library‘

3.點擊Sync Project with Gradle Files.

處理完上面這些,整個模塊的結構不會被改變,可是該模塊已經變為了庫模塊,編譯後生成的是AAR文件而不再是APK文件了。

加入庫作為應用的依賴

為了在應用模塊中使用庫模塊。你須要作例如以下的處理:

1.加入庫到工程中有兩種方式(假設你是在同樣項目中創建的庫模塊,則該模塊已經存在,您能夠跳過此步驟)

  • 加入編譯後的ARR(或JAR)文件:
    1.點擊 File > New Module.
    2.在Create New Module的窗體中,點擊 Import .JAR/.AAR Package 然後點擊 Next.
    3.輸入ARR或JAR文件所在的路徑。並點擊Finish。創建後例如以下所看到的:
    技術分享圖片

  • 導入外部庫模塊到工程中:
    1.點擊 File > New > Import Module.
    2.輸入Library模塊所在的路徑,並點擊Finish。創建後例如以下所看到的:
    技術分享圖片


這兩種引入庫的方式有所不同。假設直接引入的是庫模塊,你能夠對庫的代碼進行編輯。

可是假設導入的是AAR文件,那麽則無法進行編輯,就像JAR文件一樣。

2.當庫模塊或AAR文件引入到工程後。請確保庫被列在settings.gradle文件裏,就例如以下所看到的。當中mylibrary是庫的名稱:

include ‘:app‘, ‘:mylibrary‘

3.打開應用模塊下的build.gralde文件,並在dependencies塊中加入新的一行,使之成為該應用的依賴。例如以下片段所看到的:

dependencies {
    compile project(":mylibrary")
}

4.點擊 Sync Project with Gradle Files.

配置完上面的信息後,名為mylibrary的庫模塊就會成為應用的依賴。

然後你就能夠在應用模塊中讀取不論什麽屬於庫模塊的代碼和資源文件。

還有一種使用本地aar文件的方式

事實上我們還有一種引入本地aar文件的方式。首先在工程的下先建立一個aar文件夾,專門用於存放aar文件,然後在應用的build.gradle加入例如以下配置:

repositories {
    flatDir {
        dirs ‘../aar‘   // aar文件夾
    }
}

然後將aar文件復制到工程/aar文件夾下,在應用模塊的dependencies中加入aar引用:

compile(name: ‘mylibrary-debug‘, ext: ‘aar‘)

通過上面的配置,這樣aar就被引入過來了。這樣的方式與上面介紹的引入方式有點不同,上面作法是把引入的aar文件封裝成一個獨立的模塊,然後以compile project的方式引入。而如今的這樣的方式有點像jar包的引入方式。

註意:
依據上面的條件,假設把flatDir配置在project的gradle文件裏allprojects.repositories塊下面,發現app項目無法識別到aar文件。通過規律發現,無論aar文件放在哪裏,僅僅要在app的gradle中配置flatDir都能夠被識別。可是假設flatDir配置在project的gradle中,僅僅能把aar文件放到app的模塊下才幹被識別。

生成AAR文件

我們能夠通過點擊Build > Make Project生成aar文件,aar文件會在project-name/module-name/build/outputs/aar/ 下生成。普通情況下會有兩個aar文件,一個debug版本號。一個release版本號。

當我們拿到後aar文件後。就能夠把它公布出去。其它小夥伴就能夠利用上面的方式引入aar文件到工程中了。

AAR文件解刨

AAR 文件的文件擴展名為 .aar,該文件本身就是一個zip文件,必須要包括下面內容:

  • /AndroidManifest.xml
  • /classes.jar
  • /res/
  • /R.txt (由R.java轉換而來)

此外,AAR文件可能包括下面可選條目中的一個或多個:

  • /assets/
  • /libs/name.jar
  • /jni/abi_name/name.so(當中 abi_name 是 Android 支持的 ABI 之中的一個)
  • /proguard.txt
  • /lint.jar

庫的私有資源

默認情況下庫中的全部資源都是公開狀態。也就是說同意應用模塊直接訪問。可是假設你想讓庫中的資源僅供內部使用,而不想暴露給外部。您應通過聲明一個或多個公開資源的方式來使用這樣的自己主動私有標識機制。資源包括您項目的 res/ 文件夾中的全部文件,比如圖像、布局等。

首先,在庫的res/values/下新建一個public.xml文件(假設不存在的話),然後在public.xml中定義公開的資源名。下面的演示樣例代碼能夠創建兩個名稱分別為 lib_main_layout和 mylib_public_string的公開布局資源和字符串資源:

<resources>
    <public name="lib_main_layout" type="layout"/>
    <public name="mylib_public_string" type="string"/>
</resources>

上面的定義的兩個資源表示公開狀態,能夠被外部依賴直接訪問。而沒有被定義在當中的資源都為隱式私有狀態,外部依賴無法合法訪問。

當中name為資源名,type是資源類型有:string、layout、drawable、dimen等。

註意,假設想讓庫中的全部資源都為私有的,你必須要在public.xml中定義至少一個屬性。

在外部依賴使用庫私有資源的時候,你是無法通過R點的方式進行提示的。這也為了不暴露私有資源的一種手段。假設你強制使用了該資源,編譯器會發出警告:

技術分享圖片

從上面能夠看出。lib_main_layout和mylib_public_string資源都能夠直接使用的,而未定義的都為私有資源,外部依賴使用的時候,編譯器會發出警告信息。

可是這裏有一點須要註意,使用私有資源並不會發生不論什麽錯誤。應用模塊能夠正常的使用這些私有資源。之所以提供這樣的機制,是為了告訴你。庫模塊並不想把這些資源暴露給你,可能這些資源有特殊用途之類的。假設你真想使用私有資源。而且不想編譯器發出如上的警告,你能夠把私有資源拷到自己的應用模塊下。

隱私的賦予資源私有屬性不僅能夠一定程度上防止外部使用,而且還同意你重命名或刪除私有資源時,不會影響到使用到該庫的應用模塊。私有資源不在代碼自己主動完畢和 Theme Editor 的作用範圍內,而且假設您嘗試引用私有資源,Lint 將顯示警告。

庫開發註意事項

將庫模塊引用加入至您的Android 應用模塊後,庫模塊會依據優先級的順序與應用模塊進行合並。

資源合並沖突

  1. 構建工具會將庫模塊中的資源與相關應用模塊的資源合並。

    假設在兩個模塊中均定義了同樣的資源 ID,那就默認使用應用模塊的資源。

  2. 假設多個 AAR 庫之間發生沖突,將使用依賴項列表首先列出(位於 dependencies 塊頂部)的庫中的資源。

為了避免經常使用資源 ID 的資源沖突。請使用在模塊(或在全部項目模塊)中具有唯一性的前綴或其它一致的命名方案。

我們舉個樣例來證明觀點1。觀點2感興趣的同學能夠自己驗證。

首先在庫模塊mylibraryone中定義了例如以下的string資源:

<resources>
    <string name="app_name">My Library</string>
    <string name="test_one">My name is Library</string>
    <string name="my_library">Library</string>
</resources>

通過該庫的R文件,這三個資源文件的id值為:app_name=0x7f020000、my_library=0x7f020001、test_one=0x7f020002

然後在應用模塊mytesttwo中這也定義了例如以下的string資源:

<resources>
    <string name="app_name">MyTestTwo</string>
    <string name="test_one">My name is App</string>
</resources>

請註意。當中資源名app_name 和test_one 和庫中定義的string資源名一樣。

我們把mylibraryone庫該作為mytesttwo應用的依賴,並又一次編譯。大家能夠發如今應用模塊生成了兩個R文件:

技術分享圖片

當中第一個是庫合並過來後的R文件,而第二個是應用自己的R文件。

我們對照下。兩個R文件的內容:
mylibraryone:

public final class R {
    public static final class string {
        public static final int app_name = 0x7f040000;
        public static final int my_library = 0x7f040001;
        public static final int test_one = 0x7f040002;
    }
}

mytesttwo:

public final class R {
     .....
    public static final class mipmap {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class string {
        public static final int app_name=0x7f040000;
        public static final int my_library=0x7f040001;
        public static final int test_one=0x7f040002;
    }
}

mylibraryone庫的R文件僅僅包括自己的資源,而且全部的資源值都發生了改變。而且庫中的資源id也都合並到應用的R文件裏了。

從上面的兩個文件能夠看出一個特性:

用庫的R文件和應用的R文件都能訪問到庫的資源。可是無法用庫的R文件訪問應用資源。

既然如今庫的資源和應用的資源如今進行了合並。那當我們使用test_one字符串的時候用的是哪一個呢?我們在應用模塊下直接輸出id值來瞧瞧:

Log.d("cryc","App:"+Integer.toHexString(com.example.mytesttwo.R.string.test_one)+"");
Log.d("cryc","App:"+getString(com.example.mytesttwo.R.string.test_one)+"");
Log.d("cryc","Library:"+Integer.toHexString(com.example.mylibraryone.R.string.test_one)+"");
Log.d("cryc","Library:"+getString(com.example.mylibraryone.R.string.test_one));
Log.d("cryc","Library:"+Integer.toHexString(com.example.mylibraryone.R.string.my_library));
Log.d("cryc","Library:"+getString(com.example.mylibraryone.R.string.my_library));

輸出結果:

App:7f040002
App:My name is App
Library:7f040002
Library:My name is App
Library:7f040001
Library:Library

大家能夠看出,假設庫和應用的資源名沖突了,無論使用哪個R文件。都那默認使用應用的資源。

大家也許還有疑問。假設我在庫中使用test_one資源,那究竟是使用庫的資源還是應用的資源?答案是應用的資源,由於庫被合並到應用後。庫的R文件資源id值都發生了變化。

而我們用R文件去訪問資源的時候。都是拿變化後的R文件去訪問,所以假設有資源沖突默認都是以應用資源為準。所以這裏我也能夠得出還有一個結論:

當庫和應用模塊資源沖突的情形下,無論在應用中還是在庫中使用該資源。都默認以應用資源為主。前提是應用模塊有依賴該庫模塊。

所以為了避免經常使用資源 ID 的資源沖突。請使用在模塊(或在全部項目模塊)中具有唯一性的前綴或其它一致的命名方案。

比方庫名是PullToRefresh。那麽該庫下的資源命名能夠用ptr作為前綴。

關於R文件:

R文件(R.java)是由Android 資源打包工具AAPT(Android Asset Packaging Tool))自己主動生成,包括了res文件夾下全部資源的Id。

每當創建一個新資源,會自己主動地在R文件裏加入該資源的id。我們能夠在代碼中使用該id,執行不論什麽有關該資源的操作。註意,假設我們手動刪除R文件。編譯器會自己主動創建。

R文件是一個java文件,由於它是被自己主動創建的,所以Android studio 會把它進行隱藏,詳細位置在 app/build/generated/source/r/debug

資源沖突和私有資源的問題

當Library模塊中存在私有資源,假設應用模塊資源名和私有資源名沖突了,編譯器會發出警告:

技術分享圖片

當我們在應用中使用該資源時,也會發出該警告:

技術分享圖片

盡管我們使用該資源時用的是應用模塊的資源。可是庫已經把test_one標為私有資源,為了規範化。我能夠採取例如以下措施:

  1. 在應用模塊中更換不同的資源名。不要與庫中的資源名一樣。

  2. 假設真的要使用同名資源,使用tools標記為重寫狀態:
<resources xmlns:tools="http://schemas.android.com/tools">
    <string name="app_name">MyTestTwo</string>
    <string name="test_one" tools:override="true">My name is App</string>
</resources>

此方式並無法取消私此資源是私有資源的狀態,僅僅只是取消了資源文件裏的警告而已。

asserts合並沖突

當應用依賴庫時,應用的assert文件夾會和庫的asserts文件夾進行合並,假設有同樣路徑文件,則以應用模塊的為準。比如,應用模塊存在asserts/ha.json文件,庫模塊下也有asserts/ha.json文件,由於兩個路徑一樣。當合並後apk中僅僅保留應用模塊asserts/ha.json。假設庫模塊的ha.json文件是存放在assert/json文件夾下。那麽當合並後,兩個json文件都存在。 由於它們路徑不一樣。一個是asserts/ha.json 還有一個是asserts/json/ha.json。

谷歌官方說:工具不支持在庫模塊中使用原始資源文件(保存在 assets/ 文件夾中),可是經過我的測試,在應用模塊中能夠任意使用庫中的assets資源並無不論什麽問題。

關於asserts文件夾

Android資源文件大致能夠分為兩種:

第一種是res文件夾下存放的可編譯的資源文件:這樣的資源文件系統會在R.java裏面自己主動生成該資源文件的ID,所以訪問這樣的資源文件比較簡單。通過R.XXX.ID就可以;

另外一種是assets文件夾下存放的原生資源文件:
由於系統在編譯的時候不會編譯assets下的資源文件。所以我們不能通過R.XXX.ID的方式訪問它們。

那我麽能不能通過該資源的絕對路徑去訪問它們呢?由於apk安裝之後會放在/data/app/**.apk文件夾下。以apk形式存在,asset/res和被綁定在apk裏,並不會解壓到/data/data/YourApp文件夾下去。所以我們無法直接獲取到assets的絕對路徑。由於它們根本就沒有。

還好Android系統為我們提供了一個AssetManager工具類。查看官方API可知,AssetManager提供相應用程序的原始資源文件進行訪問;這個類提供了一個低級別的API。它同意你以簡單的字節流的形式打開和讀取和應用程序綁定在一起的原始資源文件。

應用模塊的 minSdkVersion 必須大於或等於庫定義的版本號

庫作為相關應用模塊的一部分編譯,因此。庫模塊中使用的 API 必須與應用模塊支持的平臺版本號兼容。

每一個庫模塊都會創建自己的 R 類

在您構建相關應用模塊時,庫模塊將先編譯到 AAR 文件裏。然後再加入到應用模塊中。因此,每一個庫都有其自己的 R 類,並依據庫的軟件包名稱命名。

從主模塊和庫模塊生成的 R 類會在所需的全部軟件包(包括主模塊的軟件包和庫的軟件包)中創建。

庫模塊可能包括自己的 ProGuard 配置文件

通過將 ProGuard 配置文件加入到包括其 ProGuard 指令的庫,您能夠在自己的庫上啟用代碼壓縮。構建工具會為庫模塊將此文件嵌入到生成的 AAR 文件裏。在您將庫加入到應用模塊時,庫的 ProGuard 文件將附加至應用模塊的 ProGuard 配置文件 (proguard.txt)。

通過將 ProGuard 文件嵌入到您的庫模塊中。您能夠確保依賴於此庫的應用模塊不必手動更新其 ProGuard 文件就可以使用庫。當 ProGuard 在 Android 應用模塊上執行時。它會同一時候使用來自應用模塊和庫的指令,因此您不應當僅僅在庫上執行 ProGuard。

要指定您的庫的配置文件名,請將其加入到 consumerProguardFiles 方法中,此方法位於您的庫的 build.gradle 文件的 defaultConfig 塊內。比如,下面片段會將 lib-proguard-rules.txt 設置為庫的 ProGuard 配置文件:

android {
    defaultConfig {
        consumerProguardFiles ‘lib-proguard-rules.txt‘
    }
    ...
}

默認情況下,應用模塊會使用庫的公布構建,即使在使用應用模塊的調試構建類型時亦是如此。要使用庫中不同的構建類型,您必須將依賴項加入到應用的 build.gradle 文件的 dependencies 塊中。並在庫的 build.gradle 文件裏將 publishNonDefault 設置為 true。比如。您應用的 build.gradle 文件裏的下面代碼段會使應用在應用模塊於調試模式下構建時使用庫的調試構建類型,以及在應用模塊於公布模式下構建時使用庫的公布構建類型:

dependencies {
    debugCompile project(path: ‘:library‘, configuration: ‘debug‘)
    releaseCompile project(path: ‘:library‘, configuration: ‘release‘)
}

您還必須在自己庫的 build.gradle 文件的 android 塊內加入下面代碼行。以便將此庫的非公布配置展示給使用它的項目:

android {
    ...
    publishNonDefault true
}

只是請註意。設置 publishNonDefault 會添加構建時間。
為了確保您的庫的 ProGuard 規則不會將意外的壓縮副作用施加到應用模塊,請僅包括適當規則,停用不適用於此庫的 ProGuard 功能。

嘗試協助開發人員的規則可能會與應用模塊或它的其它庫中的現有代碼沖突,因此不應包括這些規則。比如。您的庫的 ProGuard 文件能夠指定在應用模塊的壓縮期間須要保留的代碼。

註:Jack 工具鏈僅支持 ProGuard 的部分壓縮和模糊選項

參考文檔

https://developer.android.com/studio/projects/android-library.html#aar-contents
http://www.jianshu.com/p/59efa895589e
http://tikitoo.github.io/2016/05/26/android-studio-gradle-build-run-faster/
http://blog.csdn.net/z1074971432/article/details/38912747

關於創建Android Library所須要知道的一切