[TOC]
## Android SDK
將你的[Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools)放在你的home目錄或其他應用程序無關的位置。
當安裝有些包含SDK的IDE的時候,可能會將SDK放在IDE同一目錄下,當你需要升級(或重新安裝)IDE或更換的IDE時,會非常麻煩。
此外,若果你的IDE是在普通用戶,不是在root下運行,還要避免吧SDK放到一下需要sudo權限的系統級別目錄下。
## 構建系統
你的默認編譯環境應該是[Gradle](http://tools.android.com/tech-docs/new-build-system).
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](http://tools.android.com/tech-docs/new-build-system/user-guide)
**小任務**?除了(shell, Python, Perl, etc)這些腳本語言,你也可以使用Gradle 制作任務。
更多信息請參考[Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF)。
**密碼**?在做版本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.+'
}
~~~
## IDE集成開發環境和文本編輯器
**無論使用什么編輯器,一定要構建一個良好的工程結構**?編輯器每個人都有自己的
選擇,讓你的編輯器根據工程結構和構建系統運作,那是你自己的責任。
當下首推[Android Studio](https://developer.android.com/sdk/installing/studio.html),因為他是由谷歌開發,最接近Gradle,默認使用最新的工程結構,已經到beta階段
(目前已經有release 1.0了),它就是為Android開發定制的。
你也可以使用[Eclipse ADT](https://developer.android.com/sdk/installing/index.html?pkg=adt)?,但是你需要對它進行配置,因為它使用了舊的工程結構
和Ant作為構建系統。你甚至可以使用純文版編輯器如Vim,Sublime Text,或者Emacs。如果那樣的話,你需要使用Gardle和`adb`命令行。如果使用Eclipse集成Gradle
不適合你,你只是使用命令行構建工程,或遷移到Android Studio中來吧。
無論你使用何種開發工具,只要確保Gradle和新的項目結構保持官方的方式構建應用程序,避免你的編輯器配置文件加入到版本控制。例如,避免加入Ant?`build.xml`文件。
特別如果你改變Ant的配置,不要忘記保持`build.gradle`是最新和起作用的。同時,善待其他開發者,不要強制改變他們的開發工具和偏好。
## 類庫
**[Jackson](http://wiki.fasterxml.com/JacksonHome)**?是一個將java對象轉換成JSON與JSON轉化java類的類庫。[Gson](https://code.google.com/p/google-gson/)
是解決這個問題的流行方案,然而我們發現Jackson更高效,因為它支持替代的方法處理JSON:流、內存樹模型,和傳統JSON-POJO數據綁定。不過,請記住,
Jsonkson庫比起GSON更大,所以根據你的情況選擇,你可能選擇GSON來避免APP 65k個方法限制。其它選擇:?[Json-smart](https://code.google.com/p/json-smart/)?and?[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/)作為高效的網絡請求。Retrofit,Picasso和OkHttp都是有同一家公司開發(注:
是由[Square](https://github.com/square)?公司開發),所以它們能很好的在一起運行。[OkHttp 同樣可以和Volley在一起使用 Volley](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).
如若你之前有使用過Rx的經歷,開始從API響應應用它。
另外,從簡單的UI事件處理開始運用,如單擊事件或在搜索欄輸入事件。
若對你的Rx技術有信心,同時想要將它應用到你的整體架構中,那么請在復雜的部分寫好Javadocs文檔。
請記住其他不熟悉RxJava的開發人員,可能會非常難理解整個項目。
盡你的的全力幫助他們理解你的代碼和Rx。
**[Retrolambda](https://github.com/evant/gradle-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]](https://medium.com/@rotxed/dex-skys-the-limit-no-65k-methods-is-28e6cb40cf71)?[[2]](http://blog.persistent.info/2014/05/per-package-method-counts-for-androids.html)?[[3]](http://jakewharton.com/play-services-is-a-monolith/)。
當你突破65k限制之后你會看到一個致命錯誤。因此,使用一個正常范圍的類庫文件,同時使用[dex-method-counts](https://github.com/mihaip/dex-method-counts)
工具來決定哪些類庫可以再65k限制之下使用,特別的避免使用Guava類庫,因為它包含超過13k個方法。
## Activities and Fragments
[Fragments](http://developer.android.com/guide/components/fragments.html)應該作為你實現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](https://square.github.io/otto/)?或者?[greenrobot EventBus](https://github.com/greenrobot/EventBus)作為更簡潔的實現。
如果你希望避免添加另外一個類庫,RxJava同樣可以實現一個Event Bus。
* **Fragments 一般通用的不只有UI**?你可以有一個沒有界面的fragment作為Activity提供后臺工作。
進一步你可以使用這個特性來創建一個[fragment 包含改變其它fragment的邏輯](http://stackoverflow.com/questions/12363790/how-many-activities-vs-fragments/12528434#12528434)
而不是把這個邏輯放在activity中。
* **甚至ActionBar 都可以使用內部fragment來管理**?你可以選擇使用一個沒有UI界面的fragment來專門管理ActionBar,或者你可以選擇使用在每個Fragment中
添加它自己的action 來作為父Activity的ActionBar.[參考](http://www.grokkingandroid.com/adding-action-items-from-within-fragments/).
很不幸,我們不建議廣泛的使用嵌套的[fragments](https://developer.android.com/about/versions/android-4.2.html#NestedFragments),因為
有時會引起[matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/)。我們只有當它有意義(例如,在水平滑動的ViewPager在
像屏幕一樣fragment中)或者他的確是一個明智的選擇的時候才廣泛的使用fragment。
在一個架構級別,你的APP應該有一個頂級的activity來包含絕大部分業務相關的fragment。你也可能還有一些輔助的activity ,這些輔助的activity與主activity
通信很簡單限制在這兩種方法
[`Intent.setData()`](http://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri))?或?[`Intent.setAction()`](http://developer.android.com/reference/android/content/Intent.html#setAction(java.lang.String))或類似的方法。
## Java 包結構
Android 應用程序在架構上大致是Java中的[Model-View-Controller](http://en.wikipedia.org/wiki/Model%25E2%2580%2593view%25E2%2580%2593controller)結構。
在Android 中 Fragment和Activity通常上是控制器類([http://www.informit.com/articles/article.aspx?p=2126865](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 設計時布局屬性](http://tools.android.com/tips/layout-designtime-attributes),Android Studio已經提供支持,而不是硬編碼`android:text`
(譯者注:墻內也可以參考stormzhang的這篇博客[鏈接](http://stormzhang.com/devtools/2015/01/11/android-studio-tips1/))。
~~~
<?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](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views).
因此盡量保持你的視圖tree:學習如何使用[RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html),
如何?[optimize 你的布局](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).
**小心關于WebViews的問題.**?如果你必須顯示一個web視圖,
比如說對于一個新聞文章,避免做客戶端處理HTML的工作,
最好讓后端工程師協助,讓他返回一個 "*純*" HTML。
[WebViews 也能導致內存泄露](http://stackoverflow.com/questions/3130654/memory-leak-in-webview)
當保持引他們的Activity,而不是被綁定到ApplicationContext中的時候。
當使用簡單的文字或按鈕時,避免使用WebView,這時使用TextView或Buttons更好。
## 測試框架
Android SDK的測試框架還處于初級階段,特別是關于UI測試方面。Android Gradle
目前實現了一個叫[`connectedAndroidTest`](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)的測試,
它[使用一個JUnit 為Android提供的擴展插件 extension of JUnit with helpers for Android](http://developer.android.com/reference/android/test/package-summary.html).可以跑你生成的JUnit測試,
**只當做單元測試時使用?[Robolectric](http://robolectric.org/)?,views 不用**
它是一個最求提供"不連接設備的"為了加速開發的測試,
非常時候做 models 和 view models 的單元測試。
然而,使用Robolectric測試時不精確的,也不完全對UI測試。
當你對有關動畫的UI元素、對話框等,測試時會有問題,
這主要是因為你是在 “在黑暗中工作”(在沒有可控的界面情況下測試)
**[Robotium](https://code.google.com/p/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 emulator](http://www.genymotion.com/)license吧。
Genymotion 模擬器運行更快的秒幀的速度,比起典型的AVD模擬器。他有演示你APP的工具,高質量的模擬網絡連接,GPS位置,等等。它同時還有理想的連接測試。
你若涉及適配使用很多不同的設備,買一個Genymotion 版權是比你買很多真設備便宜多的。
注意:Genymotion模擬器沒有裝載所有的Google服務,如Google Play Store和Maps。你也可能需
要測試Samsung指定的API,若這樣的話你還是需要購買一個真實的Samsung設備。
## 混淆配置
[ProGuard](http://proguard.sourceforge.net/)?是一個在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 配置文件中:
~~~
-keep class com.futurice.project.MyClass { *; }
~~~
防止 ProGuard?*混淆*?一些類和成員,添加?`keepnames`:
~~~
-keepnames class com.futurice.project.MyClass { *; }
~~~
查看[this template's ProGuard config](https://github.com/futurice/android-best-practices/blob/master/templates/rx-architecture/app/proguard-rules.pro)?中的一些例子。
更多例子請參考[Proguard](http://proguard.sourceforge.net/#manual/examples.html)。
**在構建項目之初,發布一個版本**?來檢查ProGuard規則是否正確的保持了重要的部分。
同時無論何時你添加了新的類庫,做一個發布版本,同時apk在設備上跑起來測試一下。
不要等到你的app要發布 "1.0"版本了才做版本發布,那時候你可能會碰到好多意想不到的異常,需要一些時間去修復他們。
**Tips**每次發布新版本都要寫?`mapping.txt`。每發布一個版本,如果用戶遇到一個bug,同時提交了一個混淆過的堆棧跟蹤。
通過保留`mapping.txt`文件,來確定你可以調試的問題。
**DexGuard**?若果你需要核心工具來優化,和專門混淆的發布代碼,考慮使用[DexGuard](http://www.saikoa.com/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](http://www.jianshu.com/p/www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)