天天看點

Android 開發最佳實踐

這篇文章包含了很多新架構的介紹,例如Volley,Retrofit,EventBus,RxJava 等的。

原文: Best practices in Android development

Futurice公司Android開發者總結的經驗。

遵循以下準則,避免重複發明輪子。若您對開發iOS或Windows Phone 有興趣,

請看iOS Good Practices 和 Windows client Good Practices 這兩篇文章。

摘要

  • 使用 Gradle 和它推薦的工程結構
  • 把密碼和敏感資料放在gradle.properties
  • 不要自己寫 HTTP 用戶端,使用Volley或OkHttp庫
  • 使用Jackson庫解析JSON資料
  • 避免使用Guava同時使用一些類庫來避免65k method limit(一個Android程式中最多能執行65536個方法)
  • 使用 Fragments來呈現UI視圖
  • 使用 Activities 隻是為了管理 Fragments
  • Layout 布局是 XMLs代碼,組織好它們
  • 在layoutout XMLs布局時,使用styles檔案來避免使用重複的屬性
  • 使用多個style檔案來避免單一的一個大style檔案
  • 保持你的colors.xml 簡短DRY(不要重複自己),隻是定義調色闆
  • 總是使用dimens.xml DRY(不要重複自己),定義通用常數
  • 不要做一個深層次的ViewGroup
  • 在使用WebViews時避免在用戶端做處理,當心記憶體洩露
  • 使用Robolectric單元測試,Robotium 做UI測試
  • 使用Genymotion 作為你的模拟器
  • 總是使用ProGuard 和 DexGuard混淆來項目

Android SDK

将你的Android SDK放在你的home目錄或其他應用程式無關的位置。

當安裝有些包含SDK的IDE的時候,可能會将SDK放在IDE同一目錄下,當你需要更新(或重新安裝)IDE或更換的IDE時,會非常麻煩。

此外,若果你的IDE是在普通使用者,不是在root下運作,還要避免吧SDK放到一下需要sudo權限的系統級别目錄下。

建構系統

你的預設編譯環境應該是Gradle.

Ant 有很多限制,也很備援。使用Gradle,完成以下工作很友善:

  • 建構APP不同版本的變種
  • 制作簡單類似腳本的任務
  • 管理和下載下傳依賴
  • 自定義秘鑰
  • 更多

同時,Android Gradle插件作為新标準的建構系統正在被Google積極的開發。

工程結構

有兩種流行的結構:老的Ant & Eclipse ADT 工程結構,和新的Gradle & Android Studio 工程結構,

你應該選擇新的工程結構,如果你的工程還在使用老的結構,考慮放棄吧,将工程移植到新的結構。

老的結構:

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
           

主要的差別在于,新的結構明确的分開了'source sets' (

main

,

androidTest

),Gradle的一個理念。

你可以做到,例如,添加源組‘paid’和‘free’在src中,這将成為您的應用程式的付費和免費的兩種模式的源代碼。

你的項目引用第三方項目庫時(例如,library-foobar),擁有一個頂級包名

app

從第三方庫項目區分你的應用程式是非常有用的。

然後

settings.gradle

不斷引用這些庫項目,其中

app/build.gradle

可以引用。

Gradle 配置

常用結構 參考Google's guide on Gradle for Android

小任務 除了(shell, Python, Perl, etc)這些腳本語言,你也可以使用Gradle 制作任務。

更多資訊請參考Gradle's documentation。

密碼 在做版本release時你app的 

build.gradle

你需要定義 

signingConfigs

.此時你應該避免以下内容:

不要做這個 . 這會出現在版本控制中。

signingConfigs {
	release {
		storeFile file("myapp.keystore")
		storePassword "password123"
		keyAlias "thekey"
		keyPassword "password789"
	}
}
           

而是,建立一個不加入版本控制系統的

gradle.properties

檔案。

KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
           

那個檔案是gradle自動引入的,你可以在

buld.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.")
		}
	}
}
           

使用 Maven 依賴方案代替使用導入jar包方案 如果在你的項目中你明确使用率

jar檔案,那麼它們可能成為永久的版本,如

2.1.1

.下載下傳jar包更新他們是很繁瑣的,

這個問題Maven很好的解決了,這在Android Gradle建構中也是推薦的方法。你可

以指定版本的一個範圍,如

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.+'
}
           

IDEs and text editors

IDE內建開發環境和文本編輯器

無論使用什麼編輯器,一定要建構一個良好的工程結構 編輯器每個人都有自己的

選擇,讓你的編輯器根據工程結構和建構系統運作,那是你自己的責任。

當下首推Android Studio,因為他是由谷歌開發,最接近Gradle,預設使用最新的工程結構,已經到beta階段

(目前已經有release 1.0了),它就是為Android開發定制的。

你也可以使用Eclipse ADT ,但是你需要對它進行配置,因為它使用了舊的工程結構

和Ant作為建構系統。你甚至可以使用純文版編輯器如Vim,Sublime Text,或者Emacs。如果那樣的話,你需要使用Gardle和

adb

指令行。如果使用Eclipse內建Gradle

不适合你,你隻是使用指令行建構工程,或遷移到Android Studio中來吧。

無論你使用何種開發工具,隻要確定Gradle和新的項目結構保持官方的方式建構應用程式,避免你的編輯器配置檔案加入到版本控制。例如,避免加入Ant 

build.xml

檔案。

特别如果你改變Ant的配置,不要忘記保持

build.gradle

是最新和起作用的。同時,善待其他開發者,不要強制改變他們的開發工具和偏好。

類庫

Jackson 是一個将java對象轉換成JSON與JSON轉化java類的類庫。Gson

是解決這個問題的流行方案,然而我們發現Jackson更高效,因為它支援替代的方法處理JSON:流、記憶體樹模型,和傳統JSON-POJO資料綁定。不過,請記住,

Jsonkson庫比起GSON更大,是以根據你的情況選擇,你可能選擇GSON來避免APP 65k個方法限制。其它選擇: Json-smartand Boon JSON

網絡請求,緩存,圖檔 執行請求後端伺服器,有幾種互動的解決方案,你應該考慮實作你自己的網絡用戶端。使用 Volley

或Retrofit。Volley 同時提供圖檔緩存類。若果你選擇使用Retrofit,那麼考慮使用Picasso

來加載圖檔和緩存,同時使用OkHttp作為高效的網絡請求。Retrofit,Picasso和OkHttp都是有同一家公司開發(注:

是由Square 公司開發),是以它們能很好的在一起運作。OkHttp 同樣可以和Volley在一起使用 Volley.

RxJava 是函數式反應性的一個類庫,換句話說,能處理異步的事件。

這是一個強大的和有前途的模式,同時也可能會造成混淆,因為它是如此的不同。

我們建議在使用這個庫架構整個應用程式之前要謹慎考慮。

有一些項目是使用RxJava完成的,如果你需要幫助可以跟這些人取得聯系:

Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen.

我們也寫了一些部落格:

[1], [2],

[3],

[4].

如若你之前有使用過Rx的經曆,開始從API響應應用它。

另外,從簡單的UI事件處理開始運用,如單擊事件或在搜尋欄輸入事件。

若對你的Rx技術有信心,同時想要将它應用到你的整體架構中,那麼請在複雜的部分寫好Javadocs文檔。

請記住其他不熟悉RxJava的開發人員,可能會非常難了解整個項目。

盡你的的全力幫助他們了解你的代碼和Rx。

Retrolambda 是一個在Android和預JDK8平台上的使用Lambda表達式文法的Java類庫。

它有助于保持你代碼的緊湊性和可讀性,特别當你使用如RxJava函數風格程式設計時。

使用它時先安裝JDK8,在Android Studio工程結構對話框中把它設定成為SDK路徑,同時設定

JAVA8_HOME

JAVA7_HOME

環境變量,

然後在工程根目錄下配置 build.gradle:

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

同時在每個module 的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
}
           

Android Studio 提供Java8 lambdas表帶是代碼提示支援。如果你對lambdas不熟悉,隻需參照以下開始學習吧:

  • 任何隻包含一個接口的方法都是"lambda friendly"同時代碼可以被折疊成更緊湊的文法
  • 如果對參數或類似有疑問,就寫一個普通的匿名内部類,然後讓Android Status為你生成一個lambda。

當心dex方法數限制,同時避免使用過多的類庫 Android apps,當打包成一個dex檔案時,有一個65535個應用方法強硬限制[1] [2] [3]。

當你突破65k限制之後你會看到一個緻命錯誤。是以,使用一個正常範圍的類庫檔案,同時使用dex-method-counts

工具來決定哪些類庫可以再65k限制之下使用,特别的避免使用Guava類庫,因為它包含超過13k個方法。

Activities and Fragments

Fragments應該作為你實作UI界面預設選擇。你可以重複使用Fragments使用者接口來

組合成你的應用。我們強烈推薦使用Fragments而不是activity來呈現UI界面,理由如下:

  • 提供多窗格布局解決方案 Fragments 的引入主要将手機應用延伸到平闆電腦,是以在平闆電腦上你可能有A、B兩個窗格,但是在手機應用上A、B可能分别充滿

    整個螢幕。如果你的應用在最初就使用了fragments,那麼以後将你的應用适配到其他不同尺寸螢幕就會非常簡單。

  • 螢幕間資料通信 從一個Activity發送複雜資料(例如Java對象)到另外一個Activity,Android的API并沒有提供合适的方法。不過使用Fragment,你可以使用

    一個activity執行個體作為這個activity子fragments的通信通道。即使這樣比Activity與Activity間的通信好,你也想考慮使用Event Bus架構,使用如

    Otto 或者 greenrobot EventBus作為更簡潔的實作。

    如果你希望避免添加另外一個類庫,RxJava同樣可以實作一個Event Bus。

  • Fragments 一般通用的不隻有UI 你可以有一個沒有界面的fragment作為Activity提供背景工作。

    進一步你可以使用這個特性來建立一個fragment 包含改變其它fragment的邏輯

    而不是把這個邏輯放在activity中。

  • 甚至ActionBar 都可以使用内部fragment來管理 你可以選擇使用一個沒有UI界面的fragment來專門管理ActionBar,或者你可以選擇使用在每個Fragment中

    添加它自己的action 來作為父Activity的ActionBar.參考.

很不幸,我們不建議廣泛的使用嵌套的fragments,因為

有時會引起matryoshka bugs。我們隻有當它有意義(例如,在水準滑動的ViewPager在

像螢幕一樣fragment中)或者他的确是一個明智的選擇的時候才廣泛的使用fragment。

在一個架構級别,你的APP應該有一個頂級的activity來包含絕大部分業務相關的fragment。你也可能還有一些輔助的activity ,這些輔助的activity與主activity

通信很簡單限制在這兩種方法

Intent.setData()

) 或 

Intent.setAction()

)或類似的方法。

Java 包結構

Android 應用程式在架構上大緻是Java中的Model-View-Controller結構。

在Android 中 Fragment和Activity通常上是控制器類(http://www.informit.com/articles/article.aspx?p=2126865).

換句話說,他們是使用者接口的部分,同樣也是Views視圖的部分。

正是因為如此,才很難嚴格的将fragments (或者 activities) 嚴格的劃分成 控制器controlloers還是視圖 views。

最還是将它們放在自己單獨的 

fragments

 包中。隻要你遵循之前提到的建議,Activities 則可以放在頂級目錄下。

若果你規劃有2到3個以上的activity,那麼還是同樣建立一個

activities

包吧。

然而,這種架構可以看做是另一種形式的MVC,

包含要被解析API響應的JSON資料,來填充的POJO的

models

包中。

和一個

views

包來包含你的自定義視圖、通知、導航視圖,widgets等等。

擴充卡Adapter是在資料和視圖之間。然而他們通常需要通過

getView()

方法來導出一些視圖,

是以你可以将

adapters

包放在

views

包裡面。

一些控制器角色的類是應用程式級别的,同時是接近系統的。

這些類放在

managers

包下面。

一些繁雜的資料處理類,比如說"DateUtils",放在

utils

包下面。

與後端互動負責網絡處理類,放在

network

包下面。

總而言之,以最接近使用者而不是最接近後端去安排他們。

com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
   ├─ adapters
   ├─ actionbar
   ├─ widgets
   └─ notifications
           

資源檔案 Resources

  • 命名 遵循字首表明類型的習慣,形如

    type_foo_bar.xml

    。例如:

    fragment_contact_details.xml

    ,

    view_primary_button.xml

    ,

    activity_main.xml

    .

組織布局檔案 若果你不确定如何排版一個布局檔案,遵循一下規則可能會有幫助。

  • 每一個屬性一行,縮進4個空格
  • android:id

     總是作為第一個屬性
  • android:layout_****

     屬性在上邊
  • style

     屬性在底部
  • 關閉标簽

    />

    單獨起一行,有助于調整和添加新的屬性
  • 考慮使用Designtime attributes 設計時布局屬性,Android Studio已經提供支援,而不是寫死

    android:text

    (譯者注:牆内也可以參考stormzhang的這篇部落格連結)。
<?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>
           

作為一個經驗法則,

android:layout_****

屬性應該在 layout XML 中定義,同時其它屬性

android:****

 應放在 styler XML中。此規則也有例外,不過大體工作

的很好。這個思想整體是保持layout屬性(positioning, margin, sizing) 和content屬性在布局檔案中,同時将所有的外觀細節屬性(colors, padding, font)放

在style檔案中。

例外有以下這些:

  • android:id

     明顯應該在layout檔案中
  • layout檔案中

    android:orientation

    對于一個

    LinearLayout

    布局通常更有意義
  • android:text

     由于是定義内容,應該放在layout檔案中
  • 有時候将

    android:layout_width

     和 

    android:layout_height

    屬性放到一個style中作為一個通用的風格中更有意義,但是預設情況下這些應該放到layout檔案中。

使用styles 幾乎每個項目都需要适當的使用style檔案,因為對于一個視圖來說有一個重複的外觀是很常見的。

在應用中對于大多數文本内容,最起碼你應該有一個通用的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"
	/>
           

你或許需要為按鈕控件做同樣的事情,不要停止在那裡。将一組相關的和重複

android:****

的屬性放到一個通用的style中。

将一個大的style檔案分割成多個檔案 你可以有多個

styles.xml

 檔案。Android SDK支援其它檔案,

styles

這個檔案名稱并沒有作用,起作用的是在檔案

裡xml的

<style>

标簽。是以你可以有多個style檔案

styles.xml

,

style_home.xml

,

style_item_details.xml

,

styles_forms.xml

不用于資源檔案路徑需要為系統建構起的有意義,在

res/values

目錄下的檔案可以任意命名。

colors.xml

是一個調色闆 在你的

colors.xml

檔案中應該隻是映射顔色的名稱一個RGBA值,而沒有其它的。不要使用它為不同的按鈕來定義RGBA值。

不要這樣做

<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

,

應該放到一個按鈕風格中,而不是在

color.xml

檔案中。

相反,這樣做:

<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檔案 與定義顔色調色闆一樣,你同時也應該定義一個空隙間隔和字型大小的“調色闆”。

一個好的例子,如下所示:

<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>
           

布局時在寫 margins 和 paddings 時,你應該使用

spacing_****

尺寸格式來布局,而不是像對待String字元串一樣直接寫值。

這樣寫會非常有感覺,會使組織和改變風格或布局是非常容易。

避免深層次的視圖結構 有時候為了擺放一個視圖,你可能嘗試添加另一個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(這個inflate翻譯不過去,大家了解就行) 到其他views當中,也是可能會發生的。

可能會導緻一系列的問題。你可能會遇到性能問題,因為處理起需要處理一個複雜的UI樹結構。

還可能會導緻以下更嚴重的問題StackOverflowError.

是以盡量保持你的視圖tree:學習如何使用RelativeLayout,

如何 optimize 你的布局 和如何使用

<merge>

 标簽.

小心關于WebViews的問題. 如果你必須顯示一個web視圖,

比如說對于一個新聞文章,避免做用戶端處理HTML的工作,

最好讓後端工程師協助,然他反會一個 "純" HTML。

WebViews 也能導緻記憶體洩露

當保持引他們的Activity,而不是被綁定到ApplicationContext中的時候。

當使用簡單的文字或按鈕時,避免使用WebView,這時使用TextView或Buttons更好。

測試架構

Android SDK的測試架構還處于初級階段,特别是關于UI測試方面。Android Gradle

目前實作了一個叫

connectedAndroidTest

的測試,

它使用一個JUnit 為Android提供的擴充插件 extension of JUnit with helpers for Android.可以跑你生成的JUnit測試,

隻當做單元測試時使用 Robolectric ,views 不用

它是一個最求提供"不連接配接裝置的"為了加速開發的測試,

非常時候做 models 和 view models 的單元測試。

然而,使用Robolectric測試時不精确的,也不完全對UI測試。

當你對有關動畫的UI元素、對話框等,測試時會有問題,

這主要是因為你是在 “在黑暗中工作”(在沒有可控的界面情況下測試)

Robotium 使寫UI測試非常簡單。 對于UI測試你不需 Robotium 跑與裝置連接配接的測試。

但它可能會對你有益,是因為它有許多來幫助類的獲得和分析視圖,控制螢幕。

測試用例看起來像這樣簡單:

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 emulatorlicense吧。

Genymotion 模拟器運作更快的秒幀的速度,比起典型的AVD模拟器。他有示範你APP的工具,高品質的模拟網絡連接配接,GPS位置,等等。它同時還有理想的連接配接測試。

你若涉及适配使用很多不同的裝置,買一個Genymotion 版權是比你買很多真裝置便宜多的。

注意:Genymotion模拟器沒有裝載所有的Google服務,如Google Play Store和Maps。你也可能需

要測試Samsung指定的API,若這樣的話你還是需要購買一個真實的Samsung裝置。

混淆配置

ProGuard 是一個在Android項目中廣泛使用的壓縮和混淆打包的源碼的工具。

你是否使用ProGuard取決你項目的配置,當你建構一個release版本的apk時,通常你應該配置gradle檔案。

buildTypes {
	debug {
		minifyEnabled false
	}
	release {
		signingConfig signingConfigs.release
		minifyEnabled true
		proguardFiles 'proguard-rules.pro'
	}
}
           

為了決定哪些代碼應該被保留,哪些代碼應該被混淆,你不得不指定一個或多個實體類在你的代碼中。

這些實體應該是指定的類包含main方法,applets,midlets,activities,等等。

Android framework 使用一個預設的配置檔案,可以在

SDK_HOME/tools/proguard/proguard-android.txt

目錄下找到。自定義的工程指定的 project-specific 混淆規則,如在

my-project/app/proguard-rules.pro

中定義,

會被添加到預設的配置中。

關于 ProGuard 一個普遍的問題,是看應用程式是否崩潰并報

ClassNotFoundException

 或者 

NoSuchFieldException

 或類似的異常,

即使編譯是沒有警告并運作成功。

這意味着以下兩種可能:

  1. ProGuard 已經移除了類,枚舉,方法,成員變量或注解,考慮是否是必要的。
  2. ProGuard 混淆了類,枚舉,成員變量的名稱,但是這些名字又被拿原始名稱使用了,比如通過Java的反射。

檢查

app/build/outputs/proguard/release/usage.txt

檔案看有問題的對象是否被移除了。

檢查 

app/build/outputs/proguard/release/mapping.txt

 檔案看有問題的對象是否被混淆了。

In order to prevent ProGuard from stripping away needed classes or class members, add a 

keep

 options to your proguard config:

以防 ProGuard 剝離 需要的類和類成員,添加一個 

keep

選項在你的 proguard 配置檔案中:

防止 ProGuard 混淆 一些類和成員,添加 

keepnames

:

檢視this template's ProGuard config 中的一些例子。

更多例子請參考Proguard。

在建構項目之初,釋出一個版本 來檢查ProGuard規則是否正确的保持了重要的部分。

同時無論何時你添加了新的類庫,做一個釋出版本,同時apk在裝置上跑起來測試一下。

不要等到你的app要釋出 "1.0"版本了才做版本釋出,那時候你可能會碰到好多意想不到的異常,需要一些時間去修複他們。

Tips每次釋出新版本都要寫 

mapping.txt

。每釋出一個版本,如果使用者遇到一個bug,同時送出了一個混淆過的堆棧跟蹤。

通過保留

mapping.txt

檔案,來确定你可以調試的問題。

DexGuard 若果你需要核心工具來優化,和專門混淆的釋出代碼,考慮使用DexGuard,

一個商業軟體,ProGuard 也是有他們團隊開發的。

它會很容易将Dex檔案分割,來解決65K個方法限制問題。

緻謝

感謝Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton 這些人和Futurice 開發者分享他們的Android開發經驗。

License

Futurice Oy

Creative Commons Attribution 4.0 International (CC BY 4.0)

Translation

Translated to Chinese by andyiac

原文: Best practices in Android development

Futurice公司Android開發者總結的經驗。

遵循以下準則,避免重複發明輪子。若您對開發iOS或Windows Phone 有興趣,

請看iOS Good Practices 和 Windows client Good Practices 這兩篇文章。

點贊