Android應用開發編譯框架流程與IDE及Gradle概要

1 背景

建議閱讀本文以前先閱讀《Android Studio入門到精通》《Groovy腳本基礎全攻略》《Gradle腳本基礎全攻略》三篇博客做爲背景知識,這樣才能更好、更系統的串起來。本文的核心就是下圖:html

關於Gradle的Android插件本文不會過多的說明,只給一個拋磚引玉的提示,詳細使用參見文檔API及Gradle配置,其實個性化的構建配置通常都是Gradle與Groovy的編寫,與Android插件沒太多關係,因此重點還在Groovy與Gradle構建。java

不過在構建以前還須要先了解一些構建的流程和相關IDE及編譯特性,具體下面會說到。linux

【工匠若水 http://blog.csdn.net/yanbober 未經容許嚴禁轉載,請尊重做者勞動成果。私信聯繫我android

2 Android應用編譯框架

在咱們開發App時大多數時候的編譯流程都是直接經過IDE的按鈕或者命令行一步到位apk的生成,直觀上的感受好像源碼到Apk的生成只有一步,實質不是這樣的。從咱們點擊按鈕那一刻到生成apk的過程實際是很是複雜的,涉及到不少步驟,下面咱們來粗略看看。api

2-1 Android應用源碼到生成Apk流程

Android應用的編譯其實就是打包爲.apk文件,這個被打包的zpk文件實質實際上是一個壓縮包(譬如你能夠重命名後綴解壓),這個壓縮包至少包含編譯爲.class文件轉換的.dex文件、一個二進制的AndroidManifest.xml文件、編譯的資源文件resources.arsc、未編譯的資源文件等,而後在運行前再對這個壓縮包進行簽名操做便可。具體流程以下圖:數組

這裏寫圖片描述

Android構建系統是用來構建、測試、運行和封裝應用程序的一個工具箱,整個構建系統咱們能夠經過已經集成OK的AS或者命令行來使用,構建過程會涉及許多工具和流程,經過這些工具和流程生成了許多中間文件,而後最終生成了APK。下圖描述了構建過程當中涉及的不一樣工具和過程:緩存

這裏寫圖片描述

構建系統將依據配置的product flavors、build types和dependencies合併全部的資源,若是此時不一樣文件夾含有相同名稱或設置的資源則會按照以下規則進行覆蓋:dependencies覆蓋build types,build types覆蓋product flavors,product flavors覆蓋main資源文件夾。下面是對上圖典型構建過程的詳細描述:安全

  1. Android資源打包工具(AAPT)將應用的資源文件(譬如AndroidManifest.xml文件和Activity相關的XML文件等)進行編譯,生成的一個R.Java文件就是關聯咱們Java代碼與資源文件的基礎。app

  2. aidl工具將應用中全部.aidl的文件轉換爲Java接口。框架

  3. Java編譯器將應用中全部Java代碼(包括R.java和aidl接口)編譯輸出爲class類文件。

  4. dex工具將應用編譯輸出的類文件(包括第三方jar包)轉換爲Dalvik字節碼,.dex文件就能夠最終被打包如.apk文件。

  5. apkbuilder工具將全部非編譯資源(譬如images)、編譯資源和DEX文件打包成一個APK文件。

  6. 一旦.apk文件生成之後就必須進行簽名(debug或者release簽名)才能在設備上運行。

  7. 經過zipalign工具對齊APK,減小APP在設備上運行時的內存佔用。

整個構建過程生成的東東默認都在app/build目錄下(包括中間產物和最終產物)。

特別注意: 應用程序構建有一個64K方法限制說法,超過64K會拋出以下構建信息:

Unable to execute dex: method ID not in [0, 0xffff]: 65536.
  • 1
  • 1

若是撞上該問題(通常是超大型項目或者瘋狂使用開源框架等就會越界)請點我參考解決

下面這幅圖就是整個Android應用(不包含NDK部分)的構建編譯框架詳細流程說明(來源於官方):

這裏寫圖片描述

經過這幅圖能夠明顯看出來Android應用程序編譯源碼到最終的apk產物過程實際是十分複雜的,咱們平時只是把這些細則交給了IDE和構建框架而已,因此察覺不到這些細則過程。

2-2 Android應用編譯流程相關主要工具

上面簡單說明了從源碼到APK的編譯框架過程,下面簡單說說這一過程當中涉及的幾個重要的編譯相關工具,具體以下文。

2-2-1 aapt工具

aapt(Android Asset Packaging Tool)是Android資源打包工具,在SDK的tools目錄下,咱們可使用該工具查看、建立、更新ZIP格式的文件(zip、jar、apk等),也能夠將資源文件編譯成二進制文件。平時咱們不多直接使用這個工具,一般都向前面編譯流程圖介紹的同樣,編譯框架會自動幫助咱們調運這個工具。下面是aapt命令配置OK後運行的狀況:

這裏寫圖片描述

關於aapt命令工具的用法咱們直接在終端輸入aapt回車便可看見相關help文檔,這裏只給出幾個咱們比較經常使用的命令,其餘的參見help文檔。

列出壓縮包的內容 #aapt l[ist] [-v] [-a] file.{zip,jar,apk}

這裏寫圖片描述

查看APK包中指定內容 #aapt d[ump] file.{zip,jar,apk}

這裏寫圖片描述

這個比較實用,有時候想快速知道一個app的包名等信息時能夠經過該命令快捷獲取。

aapt其餘操做命令參數

aapt命令還能夠進行打包生成資源壓縮包、給壓縮包刪除或者添加指定文件等操做,這也是編譯構建系統中生成R.java等流程使用的核心工具命令。感興趣的能夠自行google經過aapt命令行去手動打包資源文件。

2-2-2 aidl工具

AIDL(Android Interface Definition Language)是一種IDL語言,用來生成能夠在Android設備上兩個進程間通訊(IPC)的代碼。若是在一個進程中要調用另外一個進程的對象則可使用AIDL生成可序列化的參數進行傳遞。咱們一般開發中都會依據AIDL規則編寫相應的aidl文件,然而咱們會發如今build目錄下會出現咱們aidl對應的java接口文件,這就是由於編譯框架經過了aidl編譯工具將.aidl文件轉換爲.java接口文件。

下面就是在SDK的tools目錄下將aidl文件轉爲java文件的aidl工具:

這裏寫圖片描述

關於aidl工具的用法很簡單,幫助文檔說的很明白了,詳細感興趣的能夠本身去嘗試一下,這裏再也不詳細說明;基本不怎麼手動去用,我反正沒仔細研究過。

2-2-3 dx工具

咱們經過aapt將資源打包同時生成R.java,經過aidl將aidl文件編譯生成對應的java文件,再經過Java編譯器將R.java、Java源碼文件、aidl的Java文件編譯爲相關的class文件。這時候對於純Java來講class字節碼就是JVM虛擬機可執行的文件了,然而對於Android的Dalvik和ART虛擬機來講class文件是沒法直接執行的,他們可執行的文件實際上是dex格式的文件,因此咱們須要經過SDK/tools目錄的dx工具將class字節碼編譯爲dex執行文件。以下是dx工具命令基本狀況:

這裏寫圖片描述

說到了dx工具就不得再也不強調幾個比較重要的概念和基本原理,具體以下詳情。

apk、dex、odex文件關係:

dex是Dalvik VM Executes(Android Dalvik執行程序,如今ART也繼續兼容使用此格式執行程序)的全稱,dex格式文件一般在Android虛擬機中執行時都會先進行優化 ,優化後的文件大小通常都會比原dex文件大。這種優化的發生時機會分兩種狀況:

  • 系統預置應用:在系統編譯後直接生成以odex爲後綴的優化文件,啓動app時不用再解包取dex,效率高,因此在發佈系統預置應用時能夠看見一個不包含dex文件的apk文件和一個相應的odex文件。

  • 非預置應用:編譯直接生成包含dex文件的apk,包含在apk文件裏的dex文件會在運行時被優化,優化後的文件將被保存在緩存中。

這也就是爲什麼系統編譯預置app源碼後會看到odex和不含dex的apk文件,而獨立app編譯後只看見內含dex的apk文件的緣由。以前在上家公司作盒子開發時有人曾經有過這個疑惑,固然我也有過!

dex文件65535異常方法限制緣由:

Android的Dalvik和ART運行時環境都可以執行dx工具生成的.dex文件,也就是說Dalvik和ART使用了同一套Dalvik指令集。經過相關資料查詢能夠知道Dalvik指令集使用了16位寄存器來保存項目中全部的方法引用,2的16次方是65536,也就是說一個dex文件最多隻能引用65536個方法,因此對於Dalvik和ART運行時環境來講都有這個侷限性。我勒個去!!!這不就是咱們有時候編譯項目時拋出Android Dex方法限制異常的緣由麼(上面也有介紹,不明白上翻回看),也就是說編譯時拋出這個異常是由於項目包含的方法太多致使的,好在Google官方也意識到了這個缺陷,因此他們給出瞭解決方案,以下:

  • 使用ProGuard清除項目中無用方法,使用相關腳本對項目中沒用到的第三方庫中的方法進行清除處理;

  • 因爲Dalvik運行時環境限制一個apk只能包含一個classes.dex文件,因此咱們可使用Multidex Support Library支持包讓一個apk裏支持多個.dex文件,這樣就能夠突破65536的限制。

dx過程當中這個錯誤很是經典,通常都出如今大量使用了第三方庫的狀況下,因此須要注意一下。

2-2-4 apkbuilder工具

關於apkbuilder工具這個叫法其實已經有些過期了,由於比較新版本的SDK中已經將apkbuilder工具去掉了,不過apkbuilder工具的實質實際上是對/android-sdk-Linux/tools/lib/sdklib.jar中ApkBuilderMain等的一個封裝而已,因此即便沒有了該工具咱們也能夠本身實現封裝,不過新的編譯框架會自動幫助咱們解決這一過程,咱們無需手動處理。該過程的實質其實和壓縮工具的性質差很少,只是它將相關資源、dex文件等打包壓縮成了一個指定壓縮方式和深度等的apk文件而已。

2-2-5 keytool與jarsigner工具

對apkbuilder打包壓縮出來的apk進行簽名的實質實際上是在應用程序的特定字段寫入特定的標記信息,以便用來表示該軟件已經經過了簽署者審覈。簽名的做用主要是識別應用的做者、檢測應用程序是否已經改變、檢測是否爲同一個應用等。

通常咱們能夠經過keytool工具生成簽名私鑰,而後經過jarsigner工具使用私鑰對應用進行簽名。不過這一過程很是簡單,這裏就再也不囉嗦了,自行腦補。

2-2-6 zipalign工具

zipalign工具能夠對打包的應用進行優化,優化過的應用在運行時執行效率能夠達到最大限度且會佔用更少的RAM(Random Access Memory)內存。zipalign對apk文件中數據進行4字節對齊,也就是說編譯器把4個字節做爲一個單位來進行操做,這樣CPU就能對代碼進行高效訪問,由於對齊後Android系統能夠經過調用mmap函數讀取文件,也就是說進程能夠像讀寫內存同樣操做咱們apk中普通文件,因此當對齊的應用在系統中執行時經過共享內存IPC讀取資源就能獲得較高的性能,若是沒有對齊處理則必須顯示的調運read等方法去操做數據,也就是說運行過程會比較緩慢且會花費更多的內存,從而致使性能降低。

關於zipalign工具的使用這裏也再也不囉嗦了,由於一般編譯框架容許咱們直接配置腳本而不用手動執行命令。

2-2-7 ProGuard工具

ProGuard是一個壓縮、優化和混淆Java字節碼class文件的工具,它能夠刪除無用的類、字段、方法、屬性及沒用的註釋等,最大限度地優化class字節碼文件。它還可使用簡短的無心義名稱來重命名已經存在的類、字段、方法和屬性。咱們一般用它來混淆最終的項目,而後稍微增長項目被反編譯的難度,固然了,對於如今的技術來講反編譯難度這個已經不是問題了,咱們仍是重點關注他的優化無用資源和簡潔替代吧。

關於ProGuard工具的使用這裏也再也不囉嗦了,由於一般編譯框架容許咱們直接配置腳本而不用手動執行命令。

2-2-8 jobb工具

其實這個工具不屬於正常編譯框架的流程,算是Android的一個拓展特性而已。從Android 2.3版本開始系統增長了一個OBB文件系統(權限訪問限制隔離文件系統)和StorageManager類用來管理外部存儲上的數據安全。

若是你以前在Android手機上安裝過《記念碑谷》或者《機械迷城》遊戲,那你就能對這裏講的jobb工具和OBB文件系統有一個很好的理解。還記不記得在安裝幾十兆大小的遊戲後你還須要下載一個兩百多兆的zip壓縮包放到文件系統的Android/obb/[GamePackageName]目錄下才能正常玩遊戲。之因此這麼作是由於咱們的遊戲工程中包含大量的資源(圖片、視頻、音樂等),直接編譯爲APK可能會高達好幾百兆,系統在安裝APK時又會對APK文件大小有一個限制,這麼大的APK文件一定會致使Android系統沒法正常安裝該APK;相信此時機智的你指定會說,咱們把這些資源直接放到SD卡上不就完了?哈哈,你想沒想過一問題,若是直接放到SD卡,系統的音樂、視頻、圖片等管理器豈不是直接能夠索引到這些東東了,那得多很差(插一句,還能夠將這些資源去掉後綴保存,這樣這些媒體庫就沒法索引了,譬如Android系統郵件應用的附件就是這麼設計的,真機智!)。好在Android 2.3的OBB文件能夠很牛叉的解決這一系列問題。

既然這樣的話,想必OBB文件系統必定會要求存儲的文件必須符合必定的格式,jobb就是解決這個問題的工具。jobb容許咱們生成加密或不加密的OBB格式擴展文件,OBB文件能夠做爲Android應用程序的擴展資源文件,獨立於APK文件存在。下面就是jobb工具的文檔:

這裏寫圖片描述

關於jobb工具這裏就不深刻說明了,通常遊戲等大資源應用開發中纔可能會考慮到這種設計,用到時再腦補也不遲,這裏知道有這麼回事就好了。

2-3 Android應用編譯Jack和Jill新工具鏈

到這裏其實你們對常規的應用編譯框架已經有了一個不錯的認識了,But問題來了,你是否是也以爲當前的Android編譯構建流程至關蛋疼(編譯構建巨慢)呢,其實Google官方彷佛也意識到了這個問題,他們還在今年的Google IO大會上給出了當前階段的一些優化交代,其中最值得嘗試和一提的亮點是Jack和Jill兩個新的編譯器(當前官方聲稱仍是Experimental試驗性的編譯器,還不夠健壯,還在bug收集階段,當前不支持註解處理,不支持Java 8等,因此仍是慎重),官方說這兩個編譯器旨在簡化安卓的編譯流程,說白了也就是嘗試加快編譯構建速度。下面先來看下使用Jack和Jill編譯器的構建流程:

這裏寫圖片描述

能夠看見,Jack是一個基於Java編譯器和ProGuard的工具,可是目前版本還不支持ProGuard的一些高級功能(譬如移除日誌代碼)。Jill將Java庫字節碼轉化成名爲jayce的中間字節碼.jack文件,Jack對Java源碼和jayce字節碼進行編譯,生成通過優化的dex字節碼。

想嚐鮮使用Jack和Jill你須要保證你的Build Tools version是21.1.1版本或者更高。在Gradle中配置以下:

android {
    ... buildToolsRevision ‘21.1.2’ defaultConfig { // Enable the experimental Jack build tools. useJack = true } ... }

總歸一句話,如今還年輕,有何成就還得觀望,看後面的發展趨勢吧,反正目前我是沒咋使用他,只是試驗性的嚐鮮了一把而已。

【工匠若水 http://blog.csdn.net/yanbober 未經容許嚴禁轉載,請尊重做者勞動成果。私信聯繫我

3 Android應用IDE及編譯相關特性

下面介紹一些使用IDE開發過程當中高效的代碼編寫特性,同時包含一些編譯相關的注意特性,具體以下會細說。

3-1 Android應用jar包與aar包

咱們在Android Studio中對一個本身的庫進行編譯時會同時生成jar與aar兩個包。他們的具體路徑以下:

包類型 在AS中的輸出路徑
jar /build/intermediates/bundles/debug[release]/classes.jar
aar /build/outputs/aar/library.aar


他們二者的區別以下(實質都是壓縮包):

包類型 描述
jar 只包含class文件,不包含資源文件,用於純Java編寫的庫。
aar 包含全部class及res等所有資源,相似UI庫。


其實關於他們兩者的區別咱們經過解壓縮便可直觀的看出來,這裏再也不敘述。

3-2 Android Tools Attributes編譯說明

關於Android應用編譯框架中還須要知道一個有用的工具屬性,那就是tools命名空間屬性,他的命名空間URI是http://schemas.android.com/tools,能夠說這個命名空間是專門爲開發者設計的,只在設計階段有效,運行階段無效。

3-2-1 綁定tools命名空間的方法

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" //綁定命名空間 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > ....

tools屬性能夠替換全部Android的屬性,只在設計階段有效,不會被帶入最終的apk中,因此運行時無效。整個tools命名空間的屬性分兩大類,一類是Lint相關的、一類是設計相關的。下面咱們列舉一下tools相關的一些實用屬性。

3-2-2 tools:ignore

告訴Lint忽略xml中某些警告。以下用法:

//忽略Lint對於多語言檢測的警告,多個能夠用逗號分開 <string name="show_all_apps" tools:ignore="MissingTranslation">All</string>

3-2-3 tools:targetApi

用來指定API等級,功能和Java文件中的@TargetApi註釋相似,值爲整數或者含義字符串。以下用法:

<GridLayout tools:targetApi="ICE_CREAM_SANDWICH" >

3-2-4 tools:locale

默認狀況下res/values/strings.xml文件中的字符串會進行拼寫檢查,若是本地不是英語則會給出警告,咱們能夠經過這樣來指定本地語言而後忽略警告。以下:

<resources xmlns:tools="http://schemas.android.com/tools" tools:locale="es">

3-2-5 tools:context

這個屬性其實就是關聯Activity屬性,在xml中添加該屬性後預覽該xml文件就能知道採用啥主題來預覽,同時關聯了Activity文件與xml文件,能夠從java文件直接跳轉索引。以下:

<android.support.v7.widget.GridLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity" ... >

3-2-6 tools:layout

該屬性用在xml的fragment節點中,在開發時告訴IDE該fragment在預覽模式下顯示的樣子。以下:

<fragment android:name="com.example.master.ItemListFragment" tools:layout="@android:layout/list_content" />

3-2-7 tools:listitem / listheader / listfooter

很明顯能夠猜到用來給ListView、AdapterView、GridView、ExpandableListView指定預覽時的item和頭底。以下:

<ListView
        android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" tools:listitem="@android:layout/simple_list_item_2" />

3-2-8 tools:showIn

該屬性被設置到一給被include的佈局的根節點上,預覽時可用。以下:

<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:showIn="@layout/activity_main" />

3-2-9 tools:menu

用來告訴IDE在預覽時當前xml佈局顯示指定的menu樣式。其實若是咱們指定了tools:context屬性,IDE會很智能的在咱們指定Activity文件中的onCreateOptionsMenu方法中尋找menu樣式預覽。固然了,咱們能夠經過該屬性覆蓋Activity中的menu預覽。以下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:menu="menu1,menu2" />

3-2-10 tools:actionBarNavMode

指定actionbar的預覽模式。可選值爲」standard」、」list」、」tabs」。以下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:actionBarNavMode="tabs" />

3-2-11 Designtime設計時屬性

tools屬性能夠替換全部Android的屬性,只在設計階段有效,不會被帶入最終的apk中,因此運行時無效。譬如咱們最多見的在寫一個TextView時不想在xml中給他初始文本內容,又想預覽,這時候就能夠用tools屬性替代。以下:

<TextView tools:text="Name:" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:visibility="invisible" />

能夠看見,tools命名空間屬性能夠加速咱們開發的靈活度和可預測度,同時在編譯框架中又會忽略這些屬性,不把他們帶入最終產物apk中,咱們能夠在開發中酌情選擇是否使用這些屬性。

3-3 Android Annotations註解說明

在Android Support Library的19.1版本中加入了一個新的註解包,這個包用來註解代碼,方便捕獲程序中存在的問題和bug,這個包內部的代碼就已經使用了該註解,在22.2版本中又增長了13個新的註解,這些註解能夠方便咱們代碼開發與調運規範和bug檢查,具體用法下面會一一講解。

3-3-1 註解Library依賴引用

annotations註解包默認不會自動被包含,不過若是使用appcompat包則會自動包含,由於appcompat包裏使用了註解。

在Android工程的Gradle文件中引入註解包以下:

dependencies { compile 'com.android.support:support-annotations:22.2.0' }

若是不是Android工程中想使用該註解包則能夠以下寫法(url爲本地android sdk的註解包路徑):

repositories { jcenter() maven { url '<your-SDK-path>/extras/android/m2repository' } }

3-3-2 執行註解

當咱們在用Android Studio和IntelliJ時若是給使用了註解的方法傳遞錯誤類型參數,則IDE會實時標記提醒錯誤。若是使用的是Gradle 1.3.0版本以上且安裝了Android M Preview Tools以上工具則能夠經過命令行調用gradle的lint任務進行檢查(nullness註解會被忽略檢查)。

3-3-3 Null空類型判斷註解

@Nullable 註解用來標註給定的參數或返回值能夠爲null。 
@NonNull 註解用來標註給定的參數或返回值不能爲null。

假設一個本地變量值爲null,且把它做爲參數傳遞給一個方法,且該方法的參數被@NonNull標註,則AS會提醒存在一個潛在的崩潰。以下:

import android.support.annotation.NonNull; import android.support.annotation.Nullable; ... /** * Add support for inflating the <fragment> tag. */ @Nullable @Override public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) { ...

3-3-4 資源類型註解

Android的資源值一般都是經過R文件映射的整型id來關聯的,也就是說獲取一個layout類型的資源參數很容易被誤傳遞一個其餘類型的資源參數,由於他們都是整型的資源id,編譯器很難區分。爲了解決這種問題可使用資源類型註解,由於註解提供類型檢查。譬以下面是一個被@LayoutRes註解的整型參數卻傳遞了一個string類型的資源參數,此時IDE會給出錯誤提示:

調運setContentView方法時傳遞錯誤參數: 
這裏寫圖片描述 
setContentView的資源註解實現方法: 
這裏寫圖片描述

實際上有不少不一樣的資源類型註解,譬如@AnimatorRes、@AnimRes、@AnyRes、@ArrayRes、@AttrRes、@BoolRes、@ColorRes、@DimenRes、@DrawableRes、@FractionRes、@IdRes、@IntegerRes、@InterpolatorRes、@LayoutRes、@MenuRes、@PluralsRes、@RawRes、@StringRes、@StyleableRes、@StyleRes、@XmlRes等,通常一個foo類型資源的相應資源類型註解就是@FooRes。除此以外,還有一個名爲@AnyRes的特殊資源類型註解,它被用來標註一個未知特殊類型的資源,且必須是一個資源類型。譬如在Resources.getResourceName(@AnyRes int resId)上使用的時候,咱們能夠經過getResources().getResourceName(R.drawable.icon)和getResources().getResourceName(R.string.app_name)等方式來使用,但卻不能經過getResources().getResourceName(42)來使用。

3-3-5 IntDef/StringDef類型註解

這種類型的註解是基於Intellij的魔數檢查機制的,由於Android開發中不少時候出於性能考慮,咱們會使用整型常量代替枚舉類型。譬如AppCompat庫裏的一個例子:

import android.support.annotation.IntDef; ... public abstract class ActionBar { ... @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS}) @Retention(RetentionPolicy.SOURCE) public @interface NavigationMode {} public static final int NAVIGATION_MODE_STANDARD = 0; public static final int NAVIGATION_MODE_LIST = 1; public static final int NAVIGATION_MODE_TABS = 2; @NavigationMode public abstract int getNavigationMode(); public abstract void setNavigationMode(@NavigationMode int mode);

上面非註解的部分是本來的代碼和API,咱們經過@interface建立了一個新註解NavigationMode,而且用@IntDef標註它可能的取值,還添加了@Retention(RetentionPolicy.SOURCE)告訴編譯器這個新定義的註解不須要被記錄在生成的.class文件中;使用這個註解後,若是咱們返回或傳遞的參數不在指定的常量值中則IDE會給出明顯的錯誤提示。

咱們也能夠指定整型常量爲一個標記性質的類型,由於這樣就能夠經過|、&等操做符同時傳遞多個整型常量。以下例子:

@IntDef(flag=true, value={ DISPLAY_USE_LOGO, DISPLAY_SHOW_HOME, DISPLAY_HOME_AS_UP, DISPLAY_SHOW_TITLE, DISPLAY_SHOW_CUSTOM }) @Retention(RetentionPolicy.SOURCE) public @interface DisplayOptions {}

@StringDef和@IntDef的做用基本上同樣,不一樣的是它針對的是字符串。關於類型註解更多信息點我獲取

3-3-6 線程註解@UiThread、@WorkerThread等

線程註解是在Support Library 22.2及更高版本中才被支持的,若是咱們的方法只能在指定的線程類型中被調用,則就可使用如下4個註解來標註它們:

  • @UiThread

  • @MainThread

  • @WorkerThread

  • @BinderThread

若是一個類中的全部方法都有相同的線程需求則能夠註解類自己。

關於線程註解使用的一個例子是AsyncTask,以下:

@WorkerThread protected abstract Result doInBackground(Params... params); @MainThread protected void onProgressUpdate(Progress... values) { }

若是在使用該方法時沒有按照註解線程執行則會報錯,以下: 
這裏寫圖片描述

3-3-7 約束值註解@Size、@IntRange、@FloatRange

若是咱們的參數是float或double類型且限制在一個範圍內,則可使用@FloatRange註解,以下:

public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {

若是咱們的參數是int或long類型則可使用@IntRange註解來約束值的範圍,以下:

public void setAlpha(@IntRange(from=0,to=255) int alpha) { … }

對於數據集合或字符串,咱們能夠用@Size註解來限定集合大小或字符串長度。譬如:

//集合不能爲空 @Size(min=1) //字符串最多隻能有23個字符 @Size(max=23) //數組只能有2個元素 @Size(2) //數組大小必須是2的倍數 @Size(multiple=2)

3-3-8 權限註解@RequiresPermission

若是咱們的方法調用時須要特定權限,則能夠用@RequiresPermission進行註解。以下:

@RequiresPermission(Manifest.permission.SET_WALLPAPER) public abstract void setWallpaper(Bitmap bitmap) throws IOException;

若是方法至少須要權限集合中的一個則可使用anyOf屬性。以下:

@RequiresPermission(anyOf = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}) public abstract Location getLastKnownLocation(String provider);

若是方法同時須要多個權限則可使用allOf屬性。以下:

@RequiresPermission(allOf = { Manifest.permission.READ_HISTORY_BOOKMARKS, Manifest.permission.WRITE_HISTORY_BOOKMARKS}) public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) {

對於Intent的權限能夠直接在定義的Intent常量字符串字段上標註權限需求(一般都已經被@SdkConstant註解標註過了)。以下:

@RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

有時對於ContentProvider的權限可能須要單獨的標註讀和寫權限,因此能夠用@Read或者@Write註解。以下:

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS)) @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS)) public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

3-3-9 重寫方法註解@CallSuper

若是想在重寫方法中要求必須調運父類的super方法則能夠用@CallSuper標註。以下:

@CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) {

3-3-10 @CheckResult、@VisibleForTesting、@Keep

若是咱們的方法返回一個值且指望調用者用這個值作些事情則可使用@CheckResult註解標註這個方法。

若是咱們想在測試時使用一個類、方法或者字段則能夠用@VisibleForTesting註解進行標明。

特別要說的就是咱們還在註解庫裏添加了@Keep註解,可是Gradle插件目前版本支持的還不是很好,這個註解標註的東西不會被混淆。

3-3-11 其餘註解相關

若是咱們在本身的lib庫中使用了上面這些註解,且經過Gradle構建生成了aar包,則在構建時Android Gradle插件會提取註解信息放在aar文件中供使用庫的地方使用。在aar文件中你能夠看到一個名爲annotations.zip的文件,這個文件記錄的就是使用IntelliJ擴展註解XML格式的註解信息。

PS一句,IntelliJ其實還有他本身的註解,這裏再也不詳細說明。

能夠看見,有了這種註解咱們的IDE看起來就更加的智能便捷強大了,同時也幫助咱們的編譯框架及早的發現問題,給出警告或者錯誤提示。

【工匠若水 http://blog.csdn.net/yanbober 未經容許嚴禁轉載,請尊重做者勞動成果。私信聯繫我

4 Gradle Android構建

其實Android的Gradle構建沒啥太多說的,掌握Groovy語法和Gradle框架後看看Gradle Android Plugs相關DSL特性就好了,用的時候去查API和Android DSL規則便可,重點明白結構和經常使用的DSL規則便可,這裏給出一個經常使用模板,補充經常使用實戰基礎示例參見《Android Studio入門到精通》一文的第7小節。

4-1 Gradle Android編譯構建配置基礎

Android Studio的project在project樹根和每一個module根都包含一個叫build.gradle的文件,咱們所謂的編譯構建配置其實就是經過Gradle的Android插件進行編寫腳本,大多數狀況下咱們通常只用修改相關module下的build.gradle文件就能知足需求。以下一個例子:

//添加Gradle的Android構建插件,默認包含一些task和android {...}等特定元素 apply plugin: 'com.android.application' //android元素用來配置Android編譯特有選項 android { //!!!特別注意:咱們必定要保證buildToolsVersion版本號老是高於等於compileSdkVersion版本號 //指定編譯目標 compileSdkVersion 19 //指定編譯工具版本,能夠經過SDK Manager查看 buildToolsVersion "19.0.0" //defaultConfig元素用來配置核心設置和AndroidManifest.xml文件的動態配置,這裏的值會覆蓋AndroidManifest.xml文件中相對應的值。 //!!!特別注意:defaultConfig元素值默認應用於全部build variant,除非咱們在build variant中重寫這些值 defaultConfig { //!!!App的惟一id只能在這裏聲明,不能在AndroidManifest.xml中 applicationId "com.example.my.app" minSdkVersion 8 targetSdkVersion 19 versionCode 1 versionName "1.0" } //buildTypes元素用來控制如何打包編譯app //默認狀況下編譯系統定義了兩中編譯類型:debug和release //debug類型默認會包含調試符和debug key,可是release默認不包含簽名 buildTypes { release { //經過Run ProGuard進行資源混淆!!! minifyEnabled true //getDefaultProguardFile默認獲取SDK路徑下的文件,咱們還能夠指定一個混淆規則文件,譬如proguard-rules.pro proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } //dependencies在android元素以外且之下,該元素用來指明當前module的依賴關係,構建系統會把這些依賴加入編譯classpath中 dependencies { //Module依賴,編譯時會被包含進去 compile project(":lib") //遠程二進制依賴 compile 'com.android.support:appcompat-v7:19.0.1' //本地二進制依賴 compile fileTree(dir: 'libs', include: ['*.jar']) }

有時候你會在gradle腳本中發現repositories被聲明兩次,這時候你必定有過疑惑的,下面給出這個疑惑的答案:

buildscript塊中的repositories聲明:該聲明的做用是聲明gradle腳本自身要用的資源(譬如依賴項、第三方插件、maven地址等)。

build.gradle文件中的repositories聲明:該聲明做用是項目自身須要的資源聲明。

若是想在Gradle腳本中使用第三方插件,且這些插件、類庫又不用於項目,而是支持其它Gradle腳本運行的,此時咱們就該把這種依賴聲明放在buildscript代碼塊中,Gradle在執行腳本時會優先執行buildscript代碼塊中的內容,而後執行剩餘的腳本。相反,若是咱們在項目中須要使用一些插件、類庫的話,就須要定義在buildscript塊以外的dependencies中。

4-2 Gradle Android構建配置build variants

接下來咱們來看看如何經過Gradle構建系統對一套項目工程編譯多個版本(譬如適配多設備類型、不一樣應用Market發佈等),這個構建功能很是簡單、強大和實用。

使用Product flavors進行多版本apk生成的步驟以下:

  1. 在build文件中定義product flavors;
  2. 爲每個flavor建立額外的資源目錄;
  3. 把這些資源加入工程進行指定類型編譯;

先看第一步,以下定義兩中類型的編譯配置:

... android { ... defaultConfig { ... } signingConfigs { ... } buildTypes { ... } productFlavors { demo { applicationId "com.buildsystemexample.app.demo" versionName "1.0-demo" } full { applicationId "com.buildsystemexample.app.full" versionName "1.0-full" } } } ...

上面例子建立了id與version不一樣的兩個apk;特別注意,咱們通常把不一樣product flavors相同的屬性元素都統必定義在defaultConfig元素中,由於defaultConfig元素的屬性值默認應用於全部build variant,除非咱們在build variant中重寫這些值才能夠。

接着咱們看下第二步和第三步,爲每一個flavor添加不一樣的資源。咱們能夠看見上一步中定義了不一樣的applicationId,主要區分是demo和full,也就是說咱們能夠在src目錄下爲這兩種狀況分別建立相關的資源目錄(譬如這裏建立demo和full目錄,咱們放置一些特定屬於demo和full的區分資源),完事之後在AS的面板中選擇Build Variants的不一樣類型編譯便可。

相關文章
相關標籤/搜索