【安卓開發】最佳實踐之一:安卓開發篇

這篇文章主要爲Futurice公司Android開發者總結的經驗教訓。遵循這些規範能夠避免無謂的重複勞動。若是對iOS或Windows Phone平臺的開發感興趣,請查看iOS最佳實踐文檔Windows客戶端最佳實踐文檔html

歡迎反饋,但請先閱讀反饋規範java

摘要

  • 使用Gradle和Gradle默認的項目結構
  • 將密碼和敏感數據放在gradle.properties中
  • 不要實現本身的HTTP客戶端,使用Volley或者OkHttp庫
  • 使用Jackson庫解析JSON數據
  • 因爲65K的方法空間限制,避免使用Guava並使用盡量少的庫
  • 用Fragment來顯示UI
  • Activity只用來管理Fragment
  • XML也是代碼,管理好XML代碼
  • 使用樣式來減小布局XML代碼中重複屬性
  • 將樣式寫在多個文件中,避免把樣式所有寫在單一的大文件當中
  • 保持colors.xml文件的簡短乾淨,只定義調色板
  • 一樣也保持dimens.xml簡短乾淨,只定義通用的常量
  • 避免深層級的ViewGroup
  • 避免客戶端處理WebView要顯示的內容,而且注意內存泄露
  • 使用Robolectric進行單元測試,使用Robotium進行鏈接設備(UI)的測試
  • 使用Genymotion模擬器
  • 一直使用ProGuard或者DexGuard

Android SDK

Android SDK存放在home目錄或者其餘跟應用開發無關的位置。一些IDE在安裝時包含了SDK,這時SDK可能存放在IDE的安裝目錄下。而這是很很差的作法,特別是當你須要升級(或者從新安裝)或更換IDE時。同時也要避免把SDK存放在系統目錄下,不然,當普通用戶(不是root)使用IDE時就須要獲取sudo權限。android

編譯系統

編譯系統首選Gradle。相比於Gradle,Ant更加的侷限而且更加繁瑣。使用Gradle編譯系統能夠很簡單的作到:ios

  • 將應用編譯成不一樣的版本
  • 完成簡單的相似腳本的任務
  • 管理和下載依賴
  • 自定義祕鑰倉庫
  • 其餘…

Google正積極的開發安卓Gradle插件,做爲新的標準編譯系統。git

項目結構

主要有兩個主流的項目結構:舊的Ant項目結構和Eclipse ADT項目結構,較新的Gradle和Android Studio項目結構。固然選擇新的項目結構。若是你的項目正在用舊的項目結構,考慮放棄舊的結構,轉移到新的項目結構下吧。程序員

舊項目結構:github

1
2
3
4
5
6
7
8
9
10
old-structure
├─ assets
├─ libs
├─ res
├─ src
│  └─ com /futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro

 

新的項目結構:web

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new-structure
├─ library-foobar
├─ app
│  ├─ libs
│  ├─ src
│  │  ├─ androidTest
│  │  │  └─ java
│  │  │     └─ com /futurice/project
│  │  └─ main
│  │     ├─ java
│  │     │  └─ com /futurice/project
│  │     ├─ res
│  │     └─ AndroidManifest.xml
│  ├─ build.gradle
│  └─ proguard-rules.pro
├─ build.gradle
└─ settings.gradle

新舊項目結構最大的不一樣點是新項目結構更加合理的分開了代碼集(main, androidTest)。例如,你能夠在代碼集src文件夾下添加’paid’和’free’文件夾,分別用於存放付費版應用代碼和免費版應用的代碼。shell

頂層app文件夾用於將你的應用和其餘庫(例如:library-foobar)區分開來。Settings.gradle中保存了app/build.gradle須要用到的庫的引用。編程

Gradle配置

普通項目結構。遵循Google安卓Gradle規範
簡單任務。能夠用Gradle完成一些簡單任務,而不用特意去寫(shell, Python, Perl等)腳本。具體參考Gradle文檔
密碼。你須要在build.gradle中配置應用發行版本的簽名配置。如下這些狀況是須要避免的:

不要這樣作。也許你會在版本控制系統中這樣作。

1
2
3
4
5
6
7
8
signingConfigs {
     release {
         storeFile file ( "myapp.keystore" )
         storePassword "password123"
         keyAlias "thekey"
         keyPassword "password789"
     }
}

換一種方式,新建一個gradle.properties文件,文件內容以下。注意,不要把Gradle.properties添加到版本控制系統中。

KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789

Gradle會自動導入gradle.properties文件,因此你能夠在build.gradle中這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
signingConfigs {
     release {
         try {
             storeFile file ( "myapp.keystore" )
             storePassword KEYSTORE_PASSWORD
             keyAlias "thekey"
             keyPassword KEY_PASSWORD
         }
         catch (ex) {
             throw new InvalidUserDataException( "You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties." )
         }
     }
}

使用Maven管理項目依賴,而不是直接導入jar文件。若是你顯式的導入jar文件到項目中,那這些依賴的jar文件只會是某個固定的版本,例如2.1.1。下載jar文件並管理更新這種方式笨拙不堪,而Maven徹底解決了這個問題,而且,Maven能夠集成在安卓Gradle編譯系統中。你能夠指定版本的範圍,例如2.2.+,而後Maven就會自動更新到版本範圍內的最新版本。例如:

1
2
3
4
5
6
7
8
9
dependencies {
     compile 'com.netflix.rxjava:rxjava-core:0.19.+'
     compile 'com.netflix.rxjava:rxjava-android:0.19.+'
     compile 'com.fasterxml.jackson.core:jackson-databind:2.4.+'
     compile 'com.fasterxml.jackson.core:jackson-core:2.4.+'
     compile 'com.fasterxml.jackson.core:jackson-annotations:2.4.+'
     compile 'com.squareup.okhttp:okhttp:2.0.+'
     compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.+'
}

IDE和文本編輯器

無論用什麼編輯器,它都必需要可以很好的顯示項目結構。編譯器的選擇看我的喜愛,可是編輯器必需要可以顯示項目結構和編譯。

如今最爲推薦的IDE時Android Studio,由於Android Studio由Google開發,最爲接近Gradle,默認使用新的項目結構,也終於發佈了beta版,能夠說是爲Android開發量身定作的IDE。

固然你也可使用Eclipse ADT,可是須要從新配置,由於Ecplise ADT默認使用舊的項目結構和使用Ant編譯。甚至,可使用純文本編輯器,好比Vim, Sublime Text, 或者Emacs。若是使用純文本編輯器,就須要在命令行中使用Gradle和adb。若是Eclipse集成Gradle後仍舊不能工做,你能夠選擇在命令行中編譯,或者遷移至Android Studio。

無論使用什麼IDE和文本編輯器,確保使用Gradle和新的項目結構來編譯應用程序,同時避免把編譯器的配置文件添加到版本控制系統當中。例如,避免添加Ant的配置文件build.xml。還有須要強調的一點,若是你在Ant中更改了編譯配置,不要忘記更新build.gradle,使其可以完成編譯。另外,對其餘的開發者友好一點,不要強迫他們去改變他們的工具的偏好設置。

Jackson是一個用於將對象轉換成JSON或者將JSON轉換成對象的Java庫。爲了解決JSON和對象相互轉換的問題,Gson是一個受歡迎的選擇。可是咱們發現,自從Jackson支持多種JSON處理方式:流,內存中的樹模型和傳統的JSON-POJO數據綁定,Jackson更加高效。請記住,Jackson是一個比GSON大的庫,因此請根據你本身的實際狀況作出選擇。考慮到65K的方法空間限制,你可能會偏向於選擇GSON。其餘選擇:Json-smartBoon JSON

網絡,緩存和圖片。如今已經有許多通過實踐證實的向後端服務器請求數據的解決方案。你應該考慮使用這些解決方案來實現本身的客戶端。使用Volley或者Retrofit。Volley也提供了加載和緩存圖片的幫助類。若是你選擇Retrofit,考慮使用Picasso來加載和緩存圖片,使用OkHttp來實現高效的HTTP請求。Retrofit,Picasso和OkHttp都由同一個公司實現,因此這三者契合的特別好。OkHttp也能夠和Volley配套使用。

RxJava是一個用於響應式編程的庫,也便是,處理異步事件的庫。RxJava這個範例很是強大,並且前途光明。RxJava很是不同凡響,所以使用RxJava時可能會使人迷惑。咱們推薦在把RxJava部署到整個應用前先花一些時間瞭解RxJava。如今已經有一些項目是利用RxJava來完成的,若是你須要幫助,請向這些人詢問:Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen。另外,咱們也寫了一些博客:[1][2][3][4].

若是你沒有使用Rx的經驗,請從應用Rx的響應API開始。或者,從應用Rx的UI事件處理開始,好比點擊事件或者在搜索框中的鍵盤事件。若是你對使用Rx頗有信心,想要把Rx應用到整個應用程序當中,請在比較難處理、容易使人迷惑的部分寫明Javadocs。記住,其餘不熟悉RxJava的程序員維護項目時可能會很是困難。請盡力去幫助他去理解你的代碼和Rx。

Retrolambda是一個在Android平臺或者其餘低於JDK8的平臺上處理Lambda表達式語法的Java庫。利用這個庫,能夠保持你的代碼的整潔嚴謹而且具備可讀性,特別是當你使用了函數式編程風格(functional style),例如使用了RxJava。使用前,先安裝JDK8,在Android Studio項目結構對話框中將它設置爲你的SDK路徑,設置JAVA8_HOME和JAVA7_HOME環境變量,而後在項目根目錄下build.gradle中增長如下內容:

1
2
3
dependencies {
     classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}

而後在每個模塊下的build.gradle中,增長如下內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
apply plugin: 'retrolambda'
 
android {
     compileOptions {
     sourceCompatibility JavaVersion.VERSION_1_8
     targetCompatibility JavaVersion.VERSION_1_8
}
 
retrolambda {
     jdk System.getenv( "JAVA8_HOME" )
     oldJdk System.getenv( "JAVA7_HOME" )
     javaVersion JavaVersion.VERSION_1_7
}

Android Studio支持對Java8的lambda智能提示。若是你是第一次使用lambda,從如下兩條規則開始:

  • 全部只有一個方法的接口都是「lambda友好」的,可以被轉換成更加整潔嚴謹的語法。
  • 若是你不肯定參數或者其餘信息,寫一個普通的匿名內部類,而後讓Android Studio將它轉換成一個lamdba表達式。

請注意dex方法限制,避免使用過多的庫。被打包成dex文件的安卓應用,都有一個硬性的限制:最多能有65536個方法引用[1] [2] [3]。若是你超出了這個限制,在編譯的時候你就會看到一個嚴重的編譯錯誤。所以,使用盡可能少的庫,並使用dex-method-counts工具來決定在保證不超出限制的前提下,有哪些庫可使用。特別要避免使用Guava庫,由於它包含了超過13k個方法。

在Android應用開發中,首選Fragment來顯示UI。Fragment是可重用的用戶交互界面,而且能夠將Fragment組合在一塊兒。咱們推薦使用Fragment來顯示用戶交互界面,而不是使用Activity。如下是一些理由:

  •  實現多視圖佈局。將手機應用擴展至平板的主要方法即是利用Fragment。利用Fragment,可讓視圖A和B都顯示在一個平板屏幕上,而在手機屏幕上,視圖A和B都佔一整塊屏幕。若是你的應用從一開始就用Frament來實現,那麼你很容易就能將你的應用適配到屏幕大小不一樣的設備上。
  • 屏與屏之間的通訊。 安卓API並無提供一個恰當的方法將複雜的數據(例如,一些Java對象)從一個Activity發送到另一個Activity中。可是利用Fragment時,以activity實例爲通訊管道,能夠實現該activity下的子fragment之間的通訊。即便這種方法優於Activity之間的通訊,你可能仍舊須要一個事件總線的架構,考慮使用Otto或者greenrobot EventBus
  • Fragment有更好的普適性,而不只僅只是實現UI。你能夠實現一個沒有UI的fragment,做爲activity後臺運行的「工人」。你也能夠將這個點子發揮的更淋漓盡致一點,好比建立一個fragment專門用於實現fragment的改變邏輯,而不是將這些邏輯寫在activity中。
  • 甚至ActionBar也能夠在fragment中管理。你能夠建立一個沒有UI的fragment,只用於管理ActionBar,或者在每個當前可見的fragment中把本身須要的action項添加到父activity的ActionBar上。閱讀更多內容

雖然咱們建議使用fragment,可是咱們不建議大量使用嵌套的fragment,由於可能會引發「套娃式bug」(matryoshka bugs)。只在合理的狀況下(例如,水平滑動的ViewPager中的fragment嵌套在一個模擬屏幕的fragment中)或者通過深思熟慮時,才使用嵌套的fragment。

從架構層面來說,你的應用應該有一個頂層的activity,其中activity中包含了大部分的業務相關的fragment。你也能夠有其餘的輔助activity,只要這些activity和主activity的通訊足夠簡單,可以經過Intent.setData()或者Intent.setAction()或者其餘簡單的方式實現便可。

Java包結構

Android應用程序的Java包結構能夠用基本上近似於模型-視圖-控制器結構。對於Android,Fragment和Activity實際上就是控制類。同時,這二者也是用戶交互界面的一部分,所以,這二者也是視圖。

因爲上述緣由,將fragment(或者activity)嚴格的歸類爲控制器或者是視圖是很是困難,不合理的。因此,更合理的作法是把fragment存放在專有的fragment包內。若是你遵循了前一部分的建議,那麼能夠將activity存放在最頂層的包下。若是你計劃建立多於2個或3個activity,那麼建立一個activities包。

不然(譯者注:若是沒有fragment和activity),包結構看起來就是一個典型的MVC結構。有一個models包,存放主要用於JSON解析時API返回值的POJO對象;一個views包,存放你自定義的視圖,通知,action bar視圖和小部件等。Adapter的歸類比較模糊,是處於數據和視圖之間的位置。可是,通常狀況下,adapter須要在getView()函數中引入一些視圖,因此能夠在views包下建一個adapters包來存放adpater。

一些控制類是整個應用程序都須要使用到的,也更加接近安卓系統底層。這些控制類存放在managers包下。各類數據處理類,例如「DateUtils」,存放在utils包下。負責與後端服務器進行交互的類存放在network包下。

總之,按靠近後端服務器到靠近用戶的順序排列,包結構以下:

1
2
3
4
5
6
7
8
9
10
11
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
    ├─ adapters
    ├─ actionbar
    ├─ widgets
    └─ notifications

 資源

命名。遵循以類型做爲前綴的習慣,像type_foo_bar.xml。例如:fragment_contact_details.xml,view_primary_button.xml,activity_main.xml。

管理好佈局XML代碼。若是你不肯定如何按照必定的格式來管理XML,能夠參考如下幾個習慣:

  • 一個屬性佔單獨的一行,縮進4個空格
  • android:id老是第一個屬性
  • android:layout_****屬性放在頂部
  • style屬性放在底部
  • 標籤關閉/>獨佔一行,便於調整屬性的順序和增長屬性
  • 不要在android:text中硬編碼字符串,考慮使用Android Studio中提供的Designtime attributes功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<? 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:layout_width = "match_parent"
     android:layout_height = "match_parent"
     android:orientation = "vertical"
     >
 
     < TextView
         android:id = "@+id/name"
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:layout_alignParentRight = "true"
         android:text = "@string/name"
         style = "@style/FancyText"
         />
 
     < include layout = "@layout/reusable_part" />
 
</ LinearLayout >

最重要的規則是,在佈局XML中定義android:layout_****屬性,而其餘的android:****屬性則在樣式XML中定義。這個規則有例外的狀況,可是大部分狀況下是適用的。這個規則保證只有layout屬性(positioning, margin, sizing)和內容屬性在佈局文件中,其餘的外觀屬性(colors, padding, font)則定義在樣式文件中。

例外的狀況有:

  • android:id顯然應該在佈局文件中定義
  • LinearLayout的android:orientation屬性在佈局文件中定義更爲合理
  • android:text應該在佈局文件中定義,由於它定義了特定的內容(譯者注:屬於內容屬性)
  • 有時候建立通用的樣式文件來定義android:layout_width和android:layout_height更加合理,可是通常狀況下這兩個屬性應該在佈局文件中定義。

使用樣式。在項目中,重複的view的外觀(譯者注:重複的view屬性)是很常見的,所以,基本上每一個項目都須要恰當的使用樣式。在一個應用程序中,至少應該有一個通用的文本內容的樣式。例如:

1
2
3
4
< style name = "ContentText" >
     < item name = "android:textSize" >@dimen/font_normal</ item >
     < item name = "android:textColor" >< a href = "http://www.jobbole.com/members/color/" rel = "nofollow" >@color</ a >/basic_black</ item >
</ style >

應用到TextView當中以下:

1
2
3
4
5
6
< TextView
     android:layout_width = "wrap_content"
     android:layout_height = "wrap_content"
     android:text = "@string/price"
     style = "@style/ContentText"
     />

你也可能須要給button按鈕寫一個通用的樣式, 不過不要只停留在給文本內容和按鈕寫通用樣式上。繼續的深刻應用這個思想,把View的相關的重複的屬性寫成通用的樣式。

把大的樣式文件分紅多個小樣式文件。你不必定非得只有一個styles.xml文件。Android SDK支持以非傳統方式命名的樣式文件。文件名styles並無特別的做用,起做用的只是文件中的XML標籤<style>。所以,一個項目中能夠同時有這些樣式文件styles.xmlstyles_home.xml,styles_item_details.xmlstyles_forms.xml。不像資源目錄名那樣在編譯時有特殊意義,在res/values下的文件名是任意的。

colors.xml是顏色調色板。colors.xml中應該只包含一些顏色名字到RGBA顏色值的映射。不要在colors.xml中爲不一樣的按鈕定義不一樣的顏色。

不要像下面這樣作:

1
2
3
4
5
6
7
8
9
< resources >
     < color name = "button_foreground" >#FFFFFF</ color >
     < color name = "button_background" >#2A91BD</ color >
     < color name = "comment_background_inactive" >#5F5F5F</ color >
     < color name = "comment_background_active" >#939393</ color >
     < color name = "comment_foreground" >#FFFFFF</ color >
     < color name = "comment_foreground_important" >#FF9D2F</ color >
     ...
     < color name = "comment_shadow" >#323232</ color >

若是你像上面這種形式來定義顏色,你很快便開始定義重複的RGBA顏色值。這種狀況下,須要改變基礎色值時,工做將會變得很是複雜。而且,這些顏色定義跟上下文有關,像」button」和」comment」這些,應該在按鈕的樣式文件中定義,而不是在colors.xml中定義。

你能夠這樣作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< resources >
 
     <!-- grayscale -->
     < color name = "white"     >#FFFFFF</ color >
     < color name = "gray_light" >#DBDBDB</ color >
     < color name = "gray"      >#939393</ color >
     < color name = "gray_dark" >#5F5F5F</ color >
     < color name = "black"     >#323232</ color >
 
     <!-- basic colors -->
     < color name = "green" >#27D34D</ color >
     < color name = "blue" >#2A91BD</ color >
     < color name = "orange" >#FF9D2F</ color >
     < color name = "red" >#FF432F</ color >
 
</ resources >

像應用程序的設計者要這份顏色調色板。名字不必定非得是顏色的名字,例如」green」, 「blue」等。像」brand_primary」, 「brand_secondary」, 「brand_negative」這中類型的名字也是徹底能夠接受的。以這種格式來管理顏色,在改變顏色值的時候會很方便,同時也能夠很直觀的看到使用了多少個不一樣的顏色。若是要展示一個漂亮的UI界面,減小顏色種類的使用是很重要的一點。

像管理colors.xml那樣來管理dimens.xml。一樣,你能夠定義間隔,字體大小等屬性的」調色板」,理由和管理顏色的理由同樣。下面是dimens文件的一個好樣例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
< resources >
 
     <!-- font sizes -->
     < dimen name = "font_larger" >22sp</ dimen >
     < dimen name = "font_large" >18sp</ dimen >
     < dimen name = "font_normal" >15sp</ dimen >
     < dimen name = "font_small" >12sp</ dimen >
 
     <!-- typical spacing between two views -->
     < dimen name = "spacing_huge" >40dp</ dimen >
     < dimen name = "spacing_large" >24dp</ dimen >
     < dimen name = "spacing_normal" >14dp</ dimen >
     < dimen name = "spacing_small" >10dp</ dimen >
     < dimen name = "spacing_tiny" >4dp</ dimen >
 
     <!-- typical sizes of views -->
     < dimen name = "button_height_tall" >60dp</ dimen >
     < dimen name = "button_height_normal" >40dp</ dimen >
     < dimen name = "button_height_short" >32dp</ dimen >
 
</ resources >

你應該使用(譯者注:dimens文件中定義的)spacing_****尺寸來實現視圖佈局的margin和padding屬性,而不是在佈局文件中硬編碼屬性值,這一點很像字符串的通常處理方式。這會使應用保持一致的觀感,同時在管理和更改樣式和佈局時也更加方便。

避免深層級視圖。有時候,你想要在原有的視圖xml中添加一個新的LinearLayout,以此來實現一個新的視圖。那麼,頗有可能發生下面的狀況:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
< LinearLayout
     android:layout_width = "match_parent"
     android:layout_height = "match_parent"
     android:orientation = "vertical"
     >
 
     < RelativeLayout
         ...
         >
 
         < LinearLayout
             ...
             >
 
             < LinearLayout
                 ...
                 >
 
                 < LinearLayout
                     ...
                     >
                 </ LinearLayout >
 
             </ LinearLayout >
 
         </ LinearLayout >
 
     </ RelativeLayout >
 
</ LinearLayout >

即便你沒有直接在一個佈局文件中看到上述的狀況,當你在把一個視圖填充(在Java代碼中)到另外一個視圖中時,上述的狀況也有可能發生。

這可能引起一系列的問題。可能會有性能問題,由於在這種狀況下,處理器須要處理很是複雜的UI樹。另一個更嚴重的錯誤是棧溢出錯誤

所以,儘量的減小視圖的層級:學習如何使用RelativeLayout,如何優化佈局和如如何使用<merge>標籤

謹慎處理與WebView相關的問題。當你必須顯示一個網頁時,例如一篇新聞,不要在客戶端中處理HTML,更好的作法是向後端程序員請求」純淨」的HTML代碼。當你把WebView綁定到activity上,而不是綁定到ApplicationContext上時,WebView也可能會泄露內存。不要使用WebView來展示簡單文字或者按鈕,用TextView和Button來實現。

 測試框架

Android SDK提供的測試框架仍舊不夠完善,特別是UI測試。Android Gradle如今利用一個爲安卓定製的JUnit幫助工具插件,實現了一個測試框架connectedAndroidTest來執行你建立的JUnit測試。也就是說,在進行測試時,你須要鏈接設備或者模擬器。請根據官方的測試指南[1] [2]來操做。

只用Robolectric來單元測試,不用於視圖UI測試。爲了保證開發速度,Robolectric這個測試框架致力於提供不鏈接設備時的測試,也便是適合於對模型和視圖模型的單元測試。可是,在Robolectric的框架下測試UI是不許確,不徹底的。在測試和動畫,對話框相關的UI元素時,你可能會遇到一些問題。由此,你」墜入了深淵」(測試過程當中看不到控制屏幕),這使測試變得很是複雜。

Robotium讓寫UI測試變得很是容易。在Robotium測試框架下測試UI,你不須要進行鏈接設備的測試,可是利用Robotium提供的大量的幫助工具,你能夠很是方便的分析視圖UI和控制屏幕。測試用例也很是簡單,如下是一個例子:

1
2
3
4
5
solo.sendKey(Solo.MENU);
solo.clickOnText( "More" ); // searches for the first occurence of "More" and clicks on it
solo.clickOnText( "Preferences" );
solo.clickOnText( "Edit File Extensions" );
Assert.assertTrue(solo.searchText( "rtf" ));

模擬器

若是你以開發安卓應用爲職業,那麼買一個正版的Genymotion模擬器吧。相比於AVD模擬器,Genymotion模擬器具備更高的幀率。它提供了一些工具來演示你的應用,模擬網絡鏈接質量,GPS定位等。固然,Genymotion也適合於進行鏈接設備的測試。(譯者注:爲了全面的測試)你須要買不少(但不是所有)不一樣的設備,所以花錢買一個正版的Genymotion模擬器會比買不少物理設備便宜不少。

注意:Genymotion模擬器不會實現全部的谷歌服務,例如Google Play商店和地圖。若是你須要測試三星獨有的API,那仍是有必要買一個三星的設備。

Proguard配置

通常狀況下,ProGuard用於縮減和混淆安卓項目的打包代碼。

是否使用Proguard取決於你的項目配置。大部分狀況下,當你編譯一個發行版本的apk時,你須要配置gradle來運行ProGuard。

1
2
3
4
5
6
7
8
9
10
buildTypes {
     debug {
         runProguard false
     }
     release {
         signingConfig signingConfigs.release
         runProguard true
         proguardFiles 'proguard-rules.pro'
     }
}

爲了判斷要保留哪些代碼,忽略或者混淆哪些代碼,你必須明確的指出一個或者多個代碼入口。 這些代碼入口通常爲包含有main函數的類,Java小程序(applet),移動信息設備小程序(Midlet),activity等。你在SDK_HOME/tools/proguard/proguard-android.txt能夠找到安卓框架提供的默認配置。每一個項目在my-project/app/proguard-rules.pro中自定義的proguard規則,(譯者注:執行proguard時)會被附加到默認配置上。

有一個跟ProGuard相關的常見問題,在應用程序啓動時由於ClassNotFoundException或者NoSuchFieldException或者相似的異常而崩潰,即便你在編譯命令行(例如,assmbleRelease)中成功的完成編譯而且沒有warning提示。不外乎如下兩種狀況:

  1. ProGuard認爲一些類,枚舉,方法,變量或者註解不須要,將其移除了。
  2. ProGuard混淆了類,枚舉,或者變量,可是這些類可能被經過它原來的名字間接地調用了,例如,經過Java的反射機制調用。

查看app/build/outputs/proguard/release/usage.txt,看形成崩潰問題的對象是否被移除了。查看app/build/outputs/proguard/release/mapping.txt,看形成崩潰問題的對象是否被混淆了。

爲了防止ProGuard剔除須要用到的類或者類成員,在你的proguard配置中添加一個keep項:

1
-keep class com.futurice.project.MyClass { *; }

爲了防止ProGuard混淆一些類或者類成員,添加一個keepnames項:

1
-keepnames class com.futurice.project.MyClass { *; }

在這份ProGuard配置模板中有一些例子。在ProGuard文檔中有更多的例子。

提示:把每個發行版本的mapping.txt文件都保存下來。這樣,當用戶遇到一個bug,提交了一個混淆的調用棧時,即可以根據保存的mapping.txt來調試,找到問題所在。

DexGuard。若是你須要一個不錯的工具來優化代碼,特別是通過混淆的發行版代碼,考慮使用DexGuard。DexGuard是有ProGuard團隊作的一個商業軟件。利用DexGuard,能夠很容易的分割Dex文件,解決了65k方法空間限制的問題。

感謝

感謝Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton和其餘Futurice開發者分享關於安卓開發的知識。

許可

Futurice Oy Creative Commons Attribution 4.0 International (CC BY 4.0)

相關文章
相關標籤/搜索