原文鏈接:https://github.com/futurice/android-best-practices
轉載來源:http://blog.csdn.net/asce1885/article/details/43699715
本文是Futurice公司的Android開發人員總結的最佳實踐,遵循這些準則可以避免重複製造輪子。如果你對iOS或者Windows Phone開發感興趣,那麼也請看看iOS最佳實踐(https://github.com/futurice/ios-good-practices)和Windows客戶端開發最佳實踐(https://github.com/futurice/win-client-dev-good-practices)。
概要
使用Gradle和推薦的工程結構
把密碼和敏感數據存放在gradle.properties文件中
不要自己實現HTTP客戶端,要使用Volley或者OkHttp庫
使用Jackson庫來解析JSON數據
避免使用Guava,使用少量的函數庫從而避免超出65k方法數限制
使用Fragments來表示UI界面
Activities只用來管理Fragments
佈局XML文件是代碼,要組織好它們
使用樣式文件來避免佈局XML文件中屬性的重複定義
使用多個樣式文件避免單一大樣式文件的使用
保持colors.xml文件簡短和不重複,只定義顏色值
保持dimens.xml文件不重複,並只定義通用的常量
避免ViewGroups層次結構太深
避免在客戶端側處理WebViews,謹防內存泄漏
使用Robolectric作爲單元測試的工具,Robotium作爲UI測試的工具
使用Genymotion作爲你的模擬器
總是使用ProGuard或者DexGuard
Android SDK
把你的Android SDK目錄放在電腦的主目錄或者其他跟IDE安裝目錄獨立的磁盤位置,某些IDE在安裝時就包含了Android SDK,而且可能把它放在跟IDE相同的目錄下。當你需要升級(或重新安裝)IDE,或者更換IDE時,這種做法是不好的。同樣要避免把Android SDK放在另外一個系統層級的目錄中,這樣當你的IDE在user模式下運行而不是root模式時,將需要sudo權限。
構建系統
工程結構
- old-structure
- ├─ assets
- ├─ libs
- ├─ res
- ├─ src
- │ └─ com/futurice/project
- ├─ AndroidManifest.xml
- ├─ build.gradle
- ├─ project.properties
- └─ proguard-rules.pro
old-structure
├─ assets
├─ libs
├─ res
├─ src
│ └─ com/futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro
新的工程結構如下所示:- 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
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),這是從Gradle引入的概念。例如,你可以在源碼目錄src中添加paid和free兩個子目錄,分別用來存放付費版和免費版app的源碼。
- signingConfigs {
- release {
- storeFile file("myapp.keystore")
- storePassword "password123"
- keyAlias "thekey"
- keyPassword "password789"
- }
- }
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
你應用創建一個gradle.properties文件,該文件不要添加到版本控制系統中,並設置如下:- KEYSTORE_PASSWORD=password123
- KEY_PASSWORD=password789
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
gradle會自動導入這個文件,現在你可以在build.gradle中這樣使用:- 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.")
- }
- }
- }
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文件,它們會是特定的不可變的版本,例如2.1.1。下載jar包並手動更新是很麻煩的,而這個問題Maven正好幫我們解決了,在Android Gradle構建中也建議這麼做。你可以指定某個版本範圍的jar包,例如2.1.+,這樣Maven會幫我們自動更新和這個版本模式匹配的後續升級。例子如下:- 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.+'
- }
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,因爲它是Google開發的,和Gradle耦合最好,默認使用最新的工程結構,已經處於穩定階段,是爲Android開發量身定做的IDE。
當然你也可以使用Eclipse ADT,但你需要配置它才能使用Gradle,因爲它默認使用的是舊的工程結構和使用Ant進行構建。你甚至可以使用類似Vim,Sublime Text,Emacs等文本編輯器,這種情況下你需要在命令行中使用Gradle和adb。如果你的Eclipse集成Gradle不可用,你的選擇是要麼使用命令行編譯或者把項目遷移到Android
Studio中。Android Studio是最好的選擇,因爲ADT插件已經被標記爲過時了,也就是不會再作後續維護和更新了。
無論你使用哪種方式,需保證的是按照官方的推薦使用新的工程結構和Gradle來構建你的應用,並避免把你特定於編輯器的配置文件加入到版本控制系統中。例如要避免把Ant的build.xml文件添加到版本控制系統中。特別是如果你在Ant中更改了編譯配置,不要忘了同步更新build.gradle文件。最後一點,要對其他開發人員友好,不要迫使他們修改他們所用編輯器的偏好設置。
函數庫
Jackson(http://wiki.fasterxml.com/JacksonHome)是一個把Java對象轉換爲JSON字符串或者把JSON字符串轉換成Java對象的Java函數庫。Gson(https://code.google.com/p/google-gson/)也是解決這類問題很流行的選擇之一,但我們發現Jackson更加高性能,因爲它支持多種可選的處理JSON的方式:流,內存樹模型和傳統的JSON-POJO數據綁定。儘管如此,Jackson是比Gson更大的函數庫,所以需要根據你項目的具體情況,你可能會選擇GSON來避免65k方法數限制。其他的選擇還有:Json-smart(https://code.google.com/p/json-smart/)和Boon
JSON(https://github.com/RichardHightower/boon/wiki/Boon-JSON-in-five-minutes)。
網絡,緩存和圖像。向後端服務器發起網絡請求有很多經過實戰檢驗的解決方案,你應該使用這些解決方案而不是自己實現一個。使用Volley(https://android.googlesource.com/platform/frameworks/volley)或者Retrofit(http://square.github.io/retrofit/)吧!除了網絡請求,Volley還提供了幫助類用於加載和緩存圖像。如果你選擇Retrofit,那麼可以考慮使用Picasso(http://square.github.io/picasso/)作爲加載和緩存圖像的函數庫,並結合OkHttp(http://square.github.io/okhttp/)實現高效的HTTP請求。Retrofit,Picasso和OkHttp這三款函數庫都是同一家公司開發的,所以它們能夠很好的互補。Volley也能使用OkHttp來實現網絡連接(http://stackoverflow.com/questions/24375043/how-to-implement-android-volley-with-okhttp-2-0/24951835#24951835)。
RxJava是一個響應式編程的函數庫,也就是可以處理異步事件。這是一個強大和有前途的編程範式,但由於它是如此的不同,因此會顯得不好理解。在使用這個函數庫搭建你的應用的框架時,我們建議你要保持謹慎的態度。我們有幾個項目已經使用RxJava來實現,如果你需要幫助可以聯繫以下這些人:Timo
Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen。我們已經寫了一些博客文章來進行介紹(1)http://blog.futurice.com/tech-pick-of-the-week-rx-for-net-and-rxjava-for-android;2)http://blog.futurice.com/top-7-tips-for-rxjava-on-android;3)https://gist.github.com/staltz/868e7e9bc2a7b8c1f754;4)http://blog.futurice.com/android-development-has-its-own-swift)。
如果你之前沒有使用RxJava的經驗,那麼開始時可以僅在網絡請求API的響應處使用。如果有經驗了,可以將RxJava應用在簡單UI事件的處理,例如點擊事件或者搜索框中的輸入事件。如果你對自己的RxJava技能很自信而且想把RxJava應用到整個項目架構中,那麼在代碼難以理解的部分要編寫Javadocs。要記住對RxJava不熟悉的程序員可能在維護工程的初期會很痛苦。盡你所能幫助他們理解你的代碼和RxJava。
Retrolambda(https://github.com/evant/gradle-retrolambda)是兼容在Android中和JDK8之前的Java版本中使用Lambda表達式語法的一個Java函數庫。它幫助你的代碼保持緊湊和可讀,特別是當你使用函數式編程風格時,例如使用RxJava。要使用這個庫,需要安裝JDK8,在Android Studio工程結構對話框中設置SDK的位置,並設置JAVA8_HOME和JAVA7_HOME環境變量,然後在工程的build.gradle中設置如下:
- dependencies {
- classpath 'me.tatarka:gradle-retrolambda:2.4.+'
- }
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}
接着在各個模塊的build.gradle中增加配置如下:
- 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
- }
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
}
Activities and Fragments
因此,很難嚴格界定Fragments(或者Activities)是controllers類還是views類。最好把Fragments類單獨放在fragments包裏面。如果你遵循前面段落的建議的話(只有一個主Activity),Activities可以放在頂層的包裏面。如果你計劃會存在多個activities,那麼就將Activity放在單獨的activities包裏面。
另一方面,整個包結構看起來很像經典的MVC框架,models包目錄存放網絡請求API響應經過JSON解析器填充後得到的POJOs對象。views包目錄存放自定義的views,notifications,action bar views,widgets等等。Adapters處於灰色地帶,介於data和views之間,然而,它們一般需要通過getView()函數導出視圖,所以你可以在views包中增加adapters子包。
- com.futurice.project
- ├─ network
- ├─ models
- ├─ managers
- ├─ utils
- ├─ fragments
- └─ views
- ├─ adapters
- ├─ actionbar
- ├─ widgets
- └─ notifications
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 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 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>
使用styles。幾乎所有工程都需要正確的使用styles,因爲它是讓view具有相同外觀的常見的方法。你的應用的文本內容應該至少具有一個公用的樣式,例如:
- <style name="ContentText">
- <item name="android:textSize">@dimen/font_normal</item>
- <item name="android:textColor">@color/basic_black</item>
- </style>
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
用在TextView上面如下:
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/price"
- style="@style/ContentText"
- />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
你可能需要對buttons做類似的工作,但別就此停住。繼續把相關的和重複的android:****屬性分組到公用的style文件中。把一個大的style文件細分成多個文件。你沒有必要維護單獨一個styles.xml文件,Android
SDK能很好的支持其他文件。styles文件的名字並不一定要是styles.xml,起作用的是xml文件裏面的<style>標籤。因此,你的樣式文件命名可以是styles.xml,styles_home.xml,styles_item_details.xml,styles_forms.xml等。和資源目錄名不同(編譯系統需要根據資源目錄名找到資源),res/values裏面的文件名可以隨意命名。
- <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>
<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>
- <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>
<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,減少使用的顏色種類是很重要的。dimens.xml文件跟colors.xml文件具有相同的用法。你應該定義一個典型的間距和字體大小的模版,目的基本上和colors.xml文件一樣,好的dimens.xml文件例子如下:
- <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>
<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>
佈局文件的邊距和填充應該使用spacing_****尺寸定義,而不是使用硬編碼(類似字符串硬編碼)。這樣會帶來統一的外觀,同時使得組織和修改樣式和佈局更簡單。避免過深的views層級。有時你可能會被誘導在LinearLayout中再增加一層LinearLayout,例如爲了完成一組views的排列。這種情況類似如下:
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
- <RelativeLayout
- ...
- >
- <LinearLayout
- ...
- >
- <LinearLayout
- ...
- >
- <LinearLayout
- ...
- >
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- </RelativeLayout>
- </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
即使你沒有很明顯的在Layout文件中看到這種情況,但可能最終發生在Java代碼中將某個view inflate到另外的view中。這可能會引起一些問題,你可能會遇到性能問題,因爲處理器需要處理很複雜的UI樹層級。另一個更嚴重的問題是可能會引起StackOverflowError(http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views)。
因此,儘量使你的view具有扁平的層級:學習怎樣使用RelativeLayout(https://developer.android.com/guide/topics/ui/layout/relative.html),怎樣優化佈局(http://developer.android.com/training/improving-layouts/optimizing-layout.html)和使用<merge>標籤(http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts)。
小心WebView相關的問題。當你需要展示一個web頁面時,例如新聞文章,要避免在客戶端側對HTML進行簡化處理,相反,應該向服務器端請求經過簡化後的HTML。當WebView持有所在Activity Context引用而不是Application Context引用時,也可能導致內存泄漏。要避免在簡單文本或者按鈕使用WebView,應該使用TextView和Button。
測試框架
Android SDK的測試框架還很簡單,尤其是UI測試相關的。Android Gradle目前實現了一個叫做connectedAndroidTest(http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)的測試任務,它能夠使用JUnit的Android擴展(http://developer.android.com/reference/android/test/package-summary.html)來運行你創建的JUnit測試用例。這意味着你需要連接設備或者模擬器來運行測試用例,遵循官方幫助指南來進行測試(1)http://developer.android.com/tools/testing/testing_android.html;2)http://developer.android.com/tools/testing/activity_test.html)。
使用Robolectric來進行單元測試,而不是UI測試。這是一個爲了提高開發速度,專注於提供“獨立於設備”的測試框架,尤其適用於models和view models的單元測試。但是Robolectric對於UI測試的支持是不準確和不完善的。使用Robolectric進行動畫,對話框等相關的UI元素測試時會遇到問題,你將看不到屏幕相應的UI元素被測試實時操縱,你將類似於在黑暗中行走。
Robotium簡化了UI測試。你不需要Robotium來執行UI測試用例,但它提供了很多幫助工具用來獲取和分析views,控制屏幕等,這一點對你可能很有幫助。測試用例很簡單,如下所示:
- 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"));
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"));
模擬器如果你的工作是開發android app,那麼買一個Genymotion模擬器(http://www.genymotion.com/)的licence吧。Genymotion模擬器比AVD模擬器具有更快的幀率,而且具有演示app,模擬網絡連接質量,GPS位置等工具。它也是用於連接測試的理想工具。使用Genymotion模擬器,你可以模擬很多不同類型的設備,所以購買一個Genymotion模擬器licence比買多個真實設備更划算。
要注意的是:Genymotion模擬器沒有移植所有的Google服務,例如Google Play Stoe和Google Maps。你可能需要測試三星特有的API,這時需要購買一臺真實的三星設備。
Proguard配置
在Android工程中ProGuard(http://proguard.sourceforge.net/)被用於壓縮和混淆打包後的代碼。ProGuard的使用可以在工程配置文件中設置。一般情況下當構建一個release版本的apk時,你需要配置Gradle使用ProGuard。
- buildTypes {
- debug {
- minifyEnabled false
- }
- release {
- signingConfig signingConfigs.release
- minifyEnabled true
- proguardFiles 'proguard-rules.pro'
- }
- }
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
}
爲了決定哪些代碼需要保留,哪些代碼需要丟棄或者混淆,你需要在你的代碼中指定一個或者多個入口點。這些入口點典型的就是具有main函數,applets,midlets,activities等的類。Android框架使用的默認配置文件是SDK_HOME/tools/proguard/proguard-android.txt。自定義的工程特有的proguard規則文件定義爲my-project/app/proguard-rules.pro,將會拼接到默認配置中。
- -keep class com.futurice.project.MyClass { *; }
-keep class com.futurice.project.MyClass { *; }
爲了防止ProGuard把需要的類或者類變量混淆了,要增加keepnames選項:
- -keepnames class com.futurice.project.MyClass { *; }
-keepnames class com.futurice.project.MyClass { *; }
一些例子可以從ProGuard配置模版(https://github.com/futurice/android-best-practices/blob/master/templates/rx-architecture/app/proguard-rules.pro)上面找到,更多例子參見ProGuard官方例子(http://proguard.sourceforge.net/#manual/examples.html)。在你的項目早期,執行一個release構建來檢查ProGuard規則是否正確的保持了不需要移除或者混淆的東西。當你增加新的函數庫,也要執行新的Release構建並在設備上測試生成的apk來確保沒有問題。不要等到你的app要發佈1.0版本了纔想到要執行一個release構建,這時你可能會得到及其不愉快的驚喜,並花一段時間來修復這些問題。
貼士:保存每個你發佈給最終用戶的apk包對應的mapping.txt文件。保存mapping.txt的原因在於當你的用戶上傳混淆過的crash日誌時,你可以很容易的進行調試。
DexGuard:如果你需要能對你發佈的代碼進行優化,尤其是混淆的核心工具的話,可以考慮DexGuard(http://www.saikoa.com/dexguard),這是由ProGuard同一團隊發佈的商業軟件。它還可以很容易的對分割Dex文件以解決65k函數個數限制問題。