「譯」Android最佳實踐指南——GitHub Star 7000+

Updated on 2016/2/14 更新Stetho 相關(guān)撵颊,簡書markdown不支持錨 -_-||||||||||||
Updated on 2016/1/15 表明谷歌對ADT的廢棄態(tài)度刚夺,新增段落:對于非發(fā)布版本的構(gòu)建使用不同的包名
不定時同步更新原文
歡迎轉(zhuǎn)載胖笛,但請保留譯注者鏈接:http://www.reibang.com/p/613d28a3c8a0

Lessons learned from Android developers in Futurice. Avoid reinventing the wheel by following these guidelines. If you are interested in iOS or Windows Phone development, be sure to check also our iOS Good Practices and Windows App Development Best Practices documents.

Summary 概要

使用Gradle和它推薦的項目結(jié)構(gòu)

將密碼和敏感數(shù)據(jù)放在gradle.properties中

不要自己寫Http客戶端,使用Volley或OkHttp

使用Jackson來解析JSON

由于65k方法數(shù)限制钞钙,避免使用Guava并維持數(shù)量較少的庫引用

使用Fragment呈現(xiàn)UI

Activity僅用于管理Fragment

Layout XML同樣也是代碼屿讽,好好組織它們

使用style來避免Layout XML中的重復屬性

使用多個style文件避免生成一個龐然大物

保持colors.xml簡短并謹記DRY皂冰,只在其中定義基礎(chǔ)色彩

同樣保持dimens.xml DRY,僅定義一般常量

不要制造過深的ViewGroup層級

避免WebView的客戶端側(cè)處理苛聘,并知曉它可能導致內(nèi)存泄漏

使用Robolectric做單元測試涂炎,Robotium做UI測試

模擬器使用Genymotion

總是使用 ProGuard 或 DexGuard

簡單的數(shù)據(jù)持久化使用SharedPreferences,其他的使用ContentProvider

使用Stetho進行應(yīng)用debug


Android SDK

Android SDK 放在你的home目錄或是其他應(yīng)用無關(guān)的位置设哗。某些IDE安裝的時候就包含了SDK唱捣,并且會將其放置在與IDE相同的目錄下。當你需要升級(或重裝网梢,或改變)IDE時這就成了一件壞事震缭。同時還要避免將SDK放在另一個系統(tǒng)級別的目錄下,那樣很可能會讓你在使用user權(quán)限運行IDE時需要用到sudo權(quán)限战虏。

Build system 構(gòu)建系統(tǒng)

你的默認選擇應(yīng)該是 Gradle拣宰。Ant的限制要多并且語句還更冗長。使用Gradle烦感,能夠簡單做到:

  • 使用不同的flavours或variants來構(gòu)建你的app
  • 制作簡單的script-like的tasks
  • 管理并下載依賴
  • 自定義keystores
  • 還有更多

Android's Gradle plugin同時在被Google做為新標準構(gòu)建系統(tǒng)積極開發(fā)中

Project structure 項目結(jié)構(gòu)

有兩種廣泛使用的選擇:舊式的Ant & Eclipse ADT project structure,和新式的Gradle & Android Studio project structure巡社。你應(yīng)該選擇新式,如果你還在使用舊式手趣,考慮將之做為寶貴遺產(chǎn)并轉(zhuǎn)向新式吧晌该。

Old structure:

old-structure
├─ assets
├─ libs
├─ res
├─ src
│  └─ com/futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro

New structure:

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

主要不同點在于新式使用了來自Gradle的概念,更清晰地分開了'source sets' (main, androidTest)绿渣。舉個例子朝群,你可以添加source sets 'paid' 和 'free' 到 src中作為構(gòu)建 paid 版本 和 free 版本的代碼目錄。
使用一個top-level app對于將你的app從那些需要引用的 庫項目 (e.g., library-foobar) 中區(qū)分開來很有效中符。settings.gradle中寫著那些 能被app/build.gradle引用的 庫項目 的引用姜胖。

Gradle configuration Gradle配置

General structure. Follow Google's guide on Gradle for Android

Small tasks. 與這些 (shell, Python, Perl, etc) 腳本不同,你能用Gradle來安排tasks淀散。Just follow Gradle's documentation for more details.

Passwords.
在app的 build.gradle中你需要為release版本的構(gòu)建定義signingConfigs谭期,以下為需要避免的事項:

不要這樣做。這些信息會出現(xiàn)在版本控制系統(tǒng)中吧凉。

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

與之對應(yīng)隧出,通過一個不會包含在版本控制系統(tǒng)中的gradle.properties這樣做:

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

**使用 Maven 解決依賴而非導入 jar **
如果你明確地在項目中包含特定版本的 jar(比如說 2.1.1)阀捅,下載與處理 jars 的更新將會是一件笨重累贅的事胀瞪,這個問題在 Maven 中被解決得很好,這也是 Android Gradle builds 所鼓勵的方式∑嗟看下面這個例子:

dependencies {
    compile 'com.squareup.okhttp:okhttp:2.2.0'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}

避免 Maven 的動態(tài)依賴
避免使用動態(tài)依賴的庫版本, 像是 2.1.+ 圆雁,因為這可能會導致不同的、不穩(wěn)定的構(gòu)建帆谍,或是在數(shù)次構(gòu)建之間表現(xiàn)出細微的伪朽、不可追蹤的差異行為。使用靜態(tài)版本像是2.1.1會創(chuàng)建更穩(wěn)定的汛蝙、可預(yù)期的和可重復的開發(fā)環(huán)境烈涮。

對于非發(fā)布版本的構(gòu)建使用不同的包名
debugbuild type使用applicationIdSuffix ,這能夠讓debug還有release版本的apk同時安裝在同一部設(shè)備上(如果你有任何需要的話窖剑,還能將此技巧應(yīng)用于自定義的 build 類型)坚洽。對于一個 app 的生命周期來說,當它被發(fā)布到市場之后西土,這一特性將變得非常有價值讶舰。

android {
    buildTypes {
        debug {
            applicationIdSuffix '.debug'
            versionNameSuffix '-DEBUG'
        }

        release {
            // ...
        }
    }
}

使用不同的icons來區(qū)分安裝在設(shè)備上的不同構(gòu)建版本app——比如說使用不同的色彩或是使用一個覆蓋的"debug"標簽。對于Gradle來說需了,這非常容易跳昼,你只需要將debugicon 放在app/src/debug/res,而release icon 放在 app/src/release/res肋乍。你還可以針對不同的構(gòu)建版本更改應(yīng)用名change app name鹅颊,versionName也可以改變(就像上面這個例子做的那樣)。

IDEs and text editors IDE和文本編輯器

無論使用什么編輯器住拭,它必須能針對項目結(jié)構(gòu)讓人愉快地使用
文本編輯器是一個很個人的選擇挪略,依據(jù)項目結(jié)構(gòu)和構(gòu)建系統(tǒng)來讓編輯器起到作用同時也是你的責任历帚。

目前最推薦的IDE是 Android Studio滔岳,因為它由Google開發(fā),與Gradle關(guān)系最緊密挽牢,默認使用新式項目結(jié)構(gòu)谱煤,針對Android開發(fā)量身定做。

使用 Eclipse ADT 來進行Android開發(fā)不再是一個好的選擇禽拔。2015年刘离,谷歌終止了對ADT的支持,并催促開發(fā)者盡快向Android Studio遷徙睹栖。Google ended ADT support at the end of 2015and urges users tomigrate to Android Studioas soon as possible.你也可以繼續(xù)使用它硫惕,但是需要一番配置,因為它采用舊式項目結(jié)構(gòu)與Ant構(gòu)建野来。如果 Eclipse 的 Gradle 集成令你使用得不愉快恼除,你的選擇只有使用命令行來構(gòu)建。

你也能只使用一個單純的文本編輯器像是Vim,Sublime Text, 或 Emacs。在這種情況下豁辉,你需要在命令行環(huán)境下使用 Gradle 和 adb 令野。

無論你使用什么,總得確保使用 Gradle 和新式項目結(jié)構(gòu) 來構(gòu)建應(yīng)用徽级,注意不要將編輯器相關(guān)的配置文件加到版本控制系統(tǒng)中气破。比如,避免添加Ant 的 build.xml 文件餐抢。
如果你在 Ant 中更改配置现使, 一定不要忘記讓build.gradle保持 up-to-date 和 functioning 。

還有弹澎,善待其他的開發(fā)者朴下,不要強迫他們改變他們個性化的工具配置。

Libraries 庫

Jackson 是一個用于 Object 與 JSON 間相互轉(zhuǎn)換的Java庫苦蒿。Gson 也是一個作為解決此問題廣受歡迎的存在殴胧。然而我們發(fā)現(xiàn) Jackson 表現(xiàn)更好,因為它提供了可選擇的方式來處理 JSON : streaming, in-memory tree model, and traditional JSON-POJO data binding佩迟。所以Jackson 的體積會比 GSON 要大团滥。取決于你的實際情況,你可能傾向于選擇 GSON 以避免 65k 方法數(shù)限制报强。其他選項還有: Json-smart and Boon JSON

網(wǎng)絡(luò)灸姊,緩存和圖像.
這兒有好幾種經(jīng)過實戰(zhàn)檢驗的用于后端服務(wù)器請求的解決方案,你將使用哪一種取決于你自己將要實現(xiàn)的客戶端秉溉。使用 VolleyRetrofit. Volley 額外提供了 helpers 以解決 載入和緩存圖像力惯。要是你選擇 Retrofit, 考慮用 Picasso 來做這些, 同時用 OkHttp 來完成高效 HTTP 請求。Retrofit, Picasso 和 OkHttp 這三個工具由同一家公司開發(fā)召嘶,所以他們能完美地補足彼此父晶。 OkHttp can also be used in connection with Volley.

RxJava 是一個響應(yīng)式編程框架,換句話說弄跌,處理異步事件甲喝。 它是一種強大并有前途的范例,可能從可讀性上講不是那么理想因為它是如此的不同铛只。我們推薦你在使用這個庫來構(gòu)筑整個應(yīng)用之前抱持著足夠的警惕埠胖。有一些項目通過使用 RxJava 構(gòu)筑, 如果你需要幫助可以和他們之中的人交談: Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. 我們寫了一些博文發(fā)表在這上面: [1], [2], [3], [4].

如果你從前沒有運用 Rx 的經(jīng)歷淳玩,只需要從將它作為對 API 的回應(yīng)開始即可直撤。你也可以選擇從作為簡單 UI 事件處理開始,像是 search field 上的點擊或者輸入事件蜕着。如果你對自己的 Rx 技能足夠自信并決定將它應(yīng)用到整個應(yīng)用構(gòu)筑中谋竖,一定要針對所有不易理解的部分寫Javadoc。用心記住其他不熟悉 Rx 的程序員可能會對維護項目感到無比頭大。盡你的全力來幫助他們理解你的代碼還有 Rx 圈盔。

Retrolambda 是一個用來讓 JDK8 之前的 Android 或是其他平臺支持Lambda表達式語法的庫豹芯。它用于保持你的代碼緊湊并可讀,特別是當你使用函數(shù)式風格編寫代碼比如說 RxJava 驱敲。為了使用它, 你需要安裝 JDK8, 在 Android Studio 的 Project Structure dialog 設(shè)置它作為你的 SDK Location , 然后設(shè)置環(huán)境變量 JAVA8_HOMEJAVA7_HOME , 接著在項目根目錄的 build.gradle 引用依賴:

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

并在每一個模塊的 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 lambda 的代碼協(xié)助支持铁蹈,如果你是 lambda 的新手,只需從以下建議中開始:

  • 任何只有一個方法的接口都是 "lambda friendly" 的众眨,亦即能被收縮成更緊湊的語法格式
  • 如果對 參數(shù) 或其他 的什么拿不準握牧,那么就寫一個普通的匿名內(nèi)部類并讓 Android Studio 為你將它收縮成 lambda 格式

注意 dex 方法數(shù)限制,避免使用過多庫
Android apps 當被打包成 dex file 時, 有一個固定的引用方法數(shù)限制:65536 [1] [2] [3]. 如果你超出了這一限制娩梨,就會在編譯時遇見一個致命錯誤沿腰。出于這個理由,使用盡可能少的庫狈定,并且使用這個工具 dex-method-counts 來決定使用哪些庫集合來保證低于這一限制颂龙。特別要避免使用 Guava library, 因為它包含超過 13k 方法.

Activities and Fragments

針對怎樣最佳地通過 Fragments 和 Activities 來組織 Android 架構(gòu)尚無統(tǒng)一結(jié)論,這一點不論在社區(qū)還是在 Futurice 的開發(fā)者中都是一樣纽什。Square 甚至開發(fā)了一個庫用來最大化地通過View來構(gòu)筑應(yīng)用架構(gòu) a library for building architectures mostly with Views措嵌,以此繞過對于 Fragment 的依賴,但這在社區(qū)中仍未被作為廣泛推薦的方案芦缰。

出于Android API的歷史企巢,你能自然地想到將Fragments作為屏幕上的UI碎片。換句話說让蕾,F(xiàn)ragments通常與UI相關(guān)聯(lián)浪规。Activities能被自然地想到作為控制器,從生命周期和狀態(tài)管理上的重要性來說探孝。然而笋婿,你很可能遇見角色產(chǎn)生變化的情況:activities可能被作為UI角色(delivering transitions between screens),而fragments能被單獨作為控制器 fragments might be used solely as controllers再姑。我們推薦謹慎啟航萌抵,獲知盡可能多的消息然后作出決定找御,因為無論是選擇fragments-only元镀、activities-only還是views-only架構(gòu),都存在著其缺陷霎桅。這里對于需要小心些什么有一些建議栖疑,但你需要持保留態(tài)度吸收它們:

  • 避免廣泛使用嵌套fragments nested fragments , 這可能會發(fā)生 matryoshka bugs 。 要么在有意義的時候使用嵌套fragments (舉個例子, 在一個screen-like 的fragment中需要一些 fragments 放在一個水平方向滑動的 ViewPager 中) ,要么就確保這是一個深思熟慮后的決定滔驶。
  • 避免放太多代碼在activities中遇革。任何情況下只要可能,讓它們作為輕量containers,其存在意義首要在于應(yīng)用的生命周期循環(huán)以及其他重要的Android-interfacing APIs萝快。采用單fragment的activity而不是一個單純的activity锻霎,這樣可以將UI代碼放在fragment中。當你需要改變它以重新放置到一個標簽布局或是一個多fragment表格屏幕中去的時候揪漩,這使得它能夠被復用旋恼。避免持有一個無對應(yīng)fragment的activity,除非你完全知曉這樣做的后果奄容。
  • 不要讓你的應(yīng)用的內(nèi)部工作濫用Android-level APIs冰更,像是重度依賴于Intent。這可能會影響到Android OS或是其他應(yīng)用昂勒,制造bugs或者延遲蜀细。舉個例子,如果你的應(yīng)用使用Intent作為內(nèi)部通信手段戈盈,可能會招致多秒延遲——如果它在OS啟動后緊接著被用戶打開的話奠衔。

Java packages architecture Java分包架構(gòu)

在Java分包架構(gòu)方面,Android只能算是粗略接近MVC模型Model-View-Controller塘娶。在Android中涣觉,F(xiàn)ragment和Activity是實際上的控制器類Fragment and Activity are actually controller classes。從另一方面來說血柳,它們又明顯是用戶接口的部分官册,所以同時也是視圖。

出于這一理由难捌,無法將fragments (or activities)嚴格劃分為控制器或是視圖膝宁。讓它們保持自己的fragments package更好一些。Activities能放在最高級package中只要你遵循之前部分的建議根吁。如果你計劃超過兩個或三個Activities员淫,那么再加一個 activities package。

另外击敌,也可以像經(jīng)典的MVC那樣來進行分包架構(gòu)介返,通過使用一個models package包含POJOs(這些POJOs由JSON解析器解析API responses轉(zhuǎn)化生成),和一個views package包含你的自定義Views沃斤,notifications, action bar views, widgets, etc圣蝎。Adapters算是一個麻煩,存在于數(shù)據(jù)和視圖之間衡瓶。然而徘公,典型情況是它們會通過getView()方法輸出一些視圖,因此你可以把adapters subpackage將在views里面哮针。

一些application-wide的和接近于Android系統(tǒng)的控制器類可以放置在一個managers package中关面√古郏混雜的數(shù)據(jù)處理類,像是"DateUtils"等太,放在utils package中捂齐。那些用于與后端交互的類則放在network package中。

總的來說缩抡,序列是從 closest-to-backend 到 closest-to-the-user:

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

Resources 資源

命名 遵循類型前綴慣例辛燥,像是 type_foo_bar.xml. Examples: fragment_contact_details.xml, view_primary_button.xml, activity_main.xml.

組織 layout XMLs. 如果你不確定如何格式化 layout XML, 以下慣例會有所幫助:

  • 一個屬性一行,4 空格縮進
  • android:id 總是作為第一個屬性
  • android:layout_**** 這類屬性放在最上面
  • style 屬性放在最下面
  • Tag closer /> 擁有自己的一行, 以使順序清晰和添加屬性變得容易
  • 與其使用硬編碼 android:text, 不如考慮使用設(shè)計時屬性 Designtime attributes 缝其,其受 Android Studio支持.
<?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>

作為一個經(jīng)驗法則挎塌,android:layout_****應(yīng)該在layout XML中定義,同時其他的屬性android:****應(yīng)該放在style XML中内边。這條法則會有例外榴都,但總體而言工作得很好。這個想法是為了僅將layout (positioning, margin, sizing)和content屬性放在layout files中漠其,而外觀詳情 (colors, padding, font) 放在 styles files中嘴高。

那些例外是:

  • android:id 明顯應(yīng)該放在 layout files 中
  • android:orientation 屬性對于 LinearLayout 來說一般 放在 layout files 中更有意義
  • android:text 應(yīng)該放在 layout files 中因為它定義了 content
  • 有些情況下讓 style 來定義 android:layout_widthandroid:layout_height 常量會很有用,但默認情況下它們應(yīng)該出現(xiàn)在 layout files 中

使用 styles. 幾乎每一個項目都需要適當?shù)厥褂?style和屎,因為對于 view 來說有著重復的外觀是非常常見的事拴驮,看下面這個例子:

<style name="ContentText">
    <item name="android:textSize">@dimen/font_normal</item>
    <item name="android:textColor">@color/basic_black</item>
</style>

該 style 被用于 TextViews:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/price"
    style="@style/ContentText"
    />

你很可能需要為buttons做一些相同的事,不要在這里停下柴信。從宏觀角度上提煉出一組相關(guān)聯(lián)的套啤、重復的android:****屬性到一個公共的 style 中去。

把一個大的 style 文件分割成多個
你無須拘泥于單個 styles.xml 文件随常。 Android SDK 支持其他不符合這一命名規(guī)則的文件潜沦,關(guān)于文件名 styles什么魔法也沒有,起效果的是文件中的 XML tags <style> 绪氛。因此你能擁有這樣命名的style文件 styles.xml, styles_home.xml, styles_item_details.xml, styles_forms.xml唆鸡。 不像 resource 目錄那樣命名對構(gòu)建系統(tǒng)具有意義, res/values 目錄下的文件命名完全可以隨意枣察。
注:是的争占,你可以在strings.xml中放color資源,ResourceManager通過映射可以找到它序目。

colors.xml 是一個顏色調(diào)色板 你的colors.xml中不要放其他事物臂痕,只需要映射顏色名到一個RGBA值。不要為不同類型的buttons定義RGBA值宛琅。

Don't do this:

<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值來格式化刻蟹,但這會使得在需要改變基礎(chǔ)顏色的時候變得操作復雜逗旁。同時嘿辟,這些定義與上下文緊密關(guān)聯(lián)舆瘪,像是"button" or "comment",它們應(yīng)該放置于一個button style 中红伦,而非colors.xml英古。

Instead, do this:

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

向應(yīng)用設(shè)計者詢問調(diào)色板。命名無須全都是顏色名像是"green", "blue", etc.這樣的命名也是完全可以接受的:"brand_primary", "brand_secondary", "brand_negative"昙读。像這樣格式化顏色會讓改變和重定義顏色變得容易召调,還能讓人看出一共有多少種不同的顏色被使用。通常對漂亮的UI設(shè)計來說蛮浑,減少所使用顏色的多樣性是一件重要的事唠叛。

好好對待 dimens.xml ,正如對待 colors.xml.你也應(yīng)該定義典型的間距和字號大小的“調(diào)色板”沮稚,像對于色彩的基本意圖那樣艺沼。一個好的 dimens 文件的例子像是這樣:

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

像通常對待strings那樣,你應(yīng)該使用spacing_**** dimensions 來設(shè)置 layouting, margins 和 paddings蕴掏,而不是使用硬編碼值障般。這會帶來一致的觀感,同時讓組織和改變styles及l(fā)ayouts變得簡單盛杰。

strings.xml

使用類似的命名空間來命名你的strings的keys挽荡,不要害怕在兩個或多個keys中重復某一個值。語言是很復雜的,所以命名空間是有必要的百匆,它能用于提供上下文信息還有打破模糊琐凭。

Bad

<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>

Good

<string name="error.message.network">Network error</string>
<string name="error.message.call">Call failed</string>
<string name="error.message.map">Map loading failed</string>

不要寫全大寫的string值。遵循一般的文本慣例(e.g., capitalize first character)办素。如果你需要將整句string大寫顯示,那么針對實例使用TextView中的這個屬性textAllCaps 祸穷。

Bad

<string name="error.message.call">CALL FAILED</string>

Good

<string name="error.message.call">Call failed</string>

避免深層級的 views. 有時候你只是想要再加一個LinearLayout性穿,用于完成一些views的布置。但這種情況卻可能會發(fā)生:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <RelativeLayout
        ...
        >

        <LinearLayout
            ...
            >

            <LinearLayout
                ...
                >

                <LinearLayout
                    ...
                    >
                </LinearLayout>

            </LinearLayout>

        </LinearLayout>

    </RelativeLayout>

</LinearLayout>

盡管你沒有在layout文件中直接目擊到這樣的景象雷滚,但這最終有可能發(fā)生需曾,如果你填充(in Java) views到其他views中。

一些問題可能會發(fā)生祈远。你也許遇見過性能問題呆万,因為這樣會生成一棵復雜的UI樹來讓處理器解析。另一個更嚴重的問題則是可能帶來棧溢出錯誤: StackOverflowError.

因此车份,試著讓你的views層級盡可能的扁平:學習如何使用RelativeLayout, 如何優(yōu)化你的布局 optimize your layouts 還有如何使用 <merge> tag.

清楚與 WebView 相關(guān)的問題 當你必須要顯示一個web頁面的時候谋减,比如說一篇文章,避免客戶端側(cè)的對于HTML的清理處理扫沼,更好的方式是從后端程序中直接獲取一段 "純粹的" HTML 出爹。當持有Activity的引用而非ApplicationContext時庄吼,WebView還可能導致內(nèi)存泄漏WebViews can also leak memory when they keep a reference to their Activity, instead of being bound to the ApplicationContext。避免用 WebView 來做一些簡單的文本或按鈕严就, 更好的選擇是 TextViews 或 Buttons总寻。

Test frameworks 測試框架

Android SDK's testing framework尚不完善,特別是有關(guān)于UI 測試梢为。Android Gradle實現(xiàn)了一個命名為connectedAndroidTest的測試任務(wù)渐行,它能運行你創(chuàng)造的JUnit test,參考extension of JUnit with helpers for Android.這意味著你需要連接實機或模擬器來運行測試铸董,參考官方測試的指南[1] [2]

使用 Robolectric 作為 unit tests, 而不要做 views tests 這是一個致力于提高開發(fā)速度的無須連接設(shè)備的測試框架祟印,特別適用于針對models 和 view models的單元測試。然而粟害,Robolectric對于UI tests是不完全并且錯誤的旁理。在測試以下相關(guān)UI元素時會有問題:animations, dialogs, etc,而且當你“行走于黑暗中”(沒法看到屏幕正被控制著操作)時,實際情況究竟怎樣也是非常難以理解的我磁。

Robotium 讓寫 UI tests 變得簡單 你可能不需要用Robotium來連接實機跑UI case孽文,但你仍會通過它獲取好處,因為它提供了許多helpers用于獲取及分析views以及控制屏幕夺艰。Test cases 將看起來很簡單像是這樣:

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"));

Emulators 模擬器

如果你是專業(yè)的Android apps開發(fā)者芋哭,買一個專業(yè)版 Genymotion emulator吧。Genymotion比原生模擬器運行起來有著更高的幀速郁副。它擁有一些工具用于調(diào)試你的應(yīng)用减牺,像是模擬網(wǎng)絡(luò)連接質(zhì)量,GPS位置等存谎。用于連接著進行UI test它也很理想拔疚。你還能獲取許多(不是全部)不同的虛擬設(shè)備,與購買許多實機相比Genymotion專業(yè)版的花費實在是十分便宜既荚。

警告:Genymotion emulators不支持所有的Google服務(wù)像是Google Play Store and Maps.要是你想要測試三星特征的APIs稚失,仍舊有必要擁有一臺三星實機。

Proguard configuration Proguard配置

ProGuard 常作為Android項目中縮減體積恰聘、混淆代碼的工具句各。

是否使用ProGuard取決于你的項目配置。通常你可以在gradle中像這樣配置以在構(gòu)建正式apk時使用ProGuard晴叨。

buildTypes {
    debug {
        minifyEnabled false
    }
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

為了決定某些代碼是需要保持原樣還是丟棄(注:即未實際使用凿宾,不打包,這也就是為什么使用ProGuard會減少應(yīng)用體積)還是混淆兼蕊,你需要在代碼中指出一個或多個關(guān)鍵點初厚。這些關(guān)鍵點區(qū)分出這些典型的類:with main methods, applets, midlets, activities, etc.
Android framework使用的默認混淆配置能在這里找到:SDK_HOME/tools/proguard/proguard-android.txt,使用這個配置孙技,再加上你自己在這里定義的項目限定的配置:my-project/app/proguard-rules.pro产禾,將會共同構(gòu)成最終的ProGuard混淆規(guī)則排作。

使用ProGuard經(jīng)常遇見的一個問題是應(yīng)用啟動時閃退,錯誤信息則為ClassNotFoundException or NoSuchFieldException or similar,盡管運行構(gòu)建命令成功 (i.e. assembleRelease) 且無警告下愈。
這意味著以下一到兩件事:

  1. ProGuard移除了類纽绍、枚舉蕾久、方法势似、域或注解,檢查一下哪些是不需要移除的部分僧著。
  2. ProGuard混淆(重命名)了類履因、枚舉、域盹愚,但這些都在代碼中被直接使用了它們原本的命名栅迄, i.e. through Java reflection.

檢查app/build/outputs/proguard/release/usage.txt看是否有存疑對象被移除掉了。
檢查 app/build/outputs/proguard/release/mapping.txt 看是否有存疑對象被混淆了皆怕。

防止ProGuard丟棄一些需要的類或類成員毅舆,在你的ProGuard配置中加入keep options:

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

防止ProGuard混淆一些需要的類或類成員,添加keepnames:

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

Check this template's ProGuard config for some examples.
Read more at Proguard for examples.

盡早地在你的項目中提供一個正式版本構(gòu)建 用來檢查ProGuard是否執(zhí)行正確符合預(yù)期是十分重要的事愈腾。當你引用了新庫的時候憋活,記得構(gòu)建一個正式 版本在實機上測試一下。不要等到你的應(yīng)用要發(fā)布"1.0"版本了再來構(gòu)建正式版本虱黄,你可能會遭遇一些令人不愉快的驚喜悦即,而你沒有剩下的時間去修正它們。

建議 保存好每一個你發(fā)布給你用戶的正式版本的mapping.txt file 橱乱。通過持有這些文件辜梳,你才能夠debug一些問題,當你的用戶遇見bug并提交了一份帶有混淆的stack trace.

DexGuard. 如要你需要一個 hard-core tools 來優(yōu)化并混淆正式版本代碼, 可以考慮 DexGuard, 制作 ProGuard 的團隊推出的商用軟件. 它還能輕松地分割 Dex files 以解決65k方法數(shù)限制.

Data storage 數(shù)據(jù)存儲

SharedPreferences

如果你只需持久化簡單的標志位并且你的應(yīng)用只在單進程環(huán)境下運行泳叠。SharedPreferences對你來說很可能已經(jīng)足夠了作瞄。它是不錯的默認選項。

這兒有兩個原因會讓你不想要使用SharedPreferences:

  • 性能: 你擁有大量數(shù)據(jù)或者數(shù)據(jù)本身非常復雜
  • 多進程獲取數(shù)據(jù): 你擁有運行在各自進程中的組件或是遠程服務(wù)危纫,它們需要同步數(shù)據(jù)

ContentProviders

在SharedPreferences不夠滿足你的需求的情況下粉洼,你應(yīng)該使用作為平臺標準的ContentProvider,它快速并且進程安全叶摄。

關(guān)于ContentProvider的問題則在于你在使用它之前需要寫大量的樣板似的代碼属韧,還有就是低質(zhì)量的學習指南。如果可能的話蛤吓,使用自動庫來生成ContentProvider宵喂,這樣會顯著減少勞力。such as Schematic.

你仍然需要靠你自己來寫一些解析代碼用于從Sqlite列中讀取出Object數(shù)據(jù)会傲,反之亦然锅棕。你可以序列化數(shù)據(jù)對象拙泽,像是使用Gson,并且只持有結(jié)果字串裸燎。通過這種方式你會損失一些性能顾瞻,但另一方面你將不需要為數(shù)據(jù)類中的每一個域都聲明列。

Using an ORM

我們通常不推薦使用對象關(guān)系映射庫Object-Relation Mapping library德绿,除非你有著不尋常的復雜數(shù)據(jù)和迫切的需要荷荤。它們趨向復雜并需要時間去學習。如果你決定使用ORM了移稳,要是你的應(yīng)用對 進程安全 process safe 有需求的話就要注意所使用的庫是否支持 這一特性蕴纳,許多現(xiàn)存的ORM解決方案令人驚訝地不支持。

Use Stetho

Stetho个粱,一款來自于Facebook 的Android applications debug bridge古毛,與Chrome 開發(fā)者工具集成在一起。使用它你很輕易就能檢查應(yīng)用都许,尤其是網(wǎng)絡(luò)通信稻薇。它還能讓你簡單地檢查和編輯SQLite 數(shù)據(jù)庫、shared preferences胶征。但是塞椎,你應(yīng)用確保Stetho 僅在debug版本中可用,release版本中不可用弧烤。

Thanks to

Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori M?ntyl?, Mark Voit, Andre Medeiros, Paul Houghton and other Futurice developers for sharing their knowledge on Android development.

License

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

譯者推薦閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忱屑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子暇昂,更是在濱河造成了極大的恐慌莺戒,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件急波,死亡現(xiàn)場離奇詭異从铲,居然都是意外死亡,警方通過查閱死者的電腦和手機澄暮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門名段,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泣懊,你說我怎么就攤上這事伸辟。” “怎么了馍刮?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵信夫,是天一觀的道長。 經(jīng)常有香客問我,道長静稻,這世上最難降的妖魔是什么警没? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮振湾,結(jié)果婚禮上杀迹,老公的妹妹穿的比我還像新娘。我一直安慰自己押搪,他們只是感情好树酪,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嵌言,像睡著了一般嗅回。 火紅的嫁衣襯著肌膚如雪及穗。 梳的紋絲不亂的頭發(fā)上摧茴,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音埂陆,去河邊找鬼苛白。 笑死,一個胖子當著我的面吹牛焚虱,可吹牛的內(nèi)容都是我干的购裙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼鹃栽,長吁一口氣:“原來是場噩夢啊……” “哼躏率!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起民鼓,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤薇芝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丰嘉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夯到,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年饮亏,在試婚紗的時候發(fā)現(xiàn)自己被綠了耍贾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡路幸,死狀恐怖荐开,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情简肴,我是刑警寧澤晃听,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響杂伟,放射性物質(zhì)發(fā)生泄漏移层。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一赫粥、第九天 我趴在偏房一處隱蔽的房頂上張望观话。 院中可真熱鬧,春花似錦越平、人聲如沸频蛔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晦溪。三九已至,卻和暖如春挣跋,著一層夾襖步出監(jiān)牢的瞬間三圆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工避咆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舟肉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓查库,卻偏偏與公主長得像路媚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子樊销,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 從Futurice公司Android開發(fā)者中學到的經(jīng)驗整慎。遵循以下準則,避免重復發(fā)明輪子围苫。若您對開發(fā)iOS或Wind...
    兔子吃過窩邊草閱讀 937評論 0 7
  • 轉(zhuǎn)載文章裤园,Android開發(fā)過程中的經(jīng)驗總結(jié)。原文鏈接遵循以下準則够吩,避免重復發(fā)明輪子比然。若您對開發(fā)iOS或Windo...
    王鵬程Orange閱讀 1,187評論 0 22
  • Android 開發(fā)最佳實踐 轉(zhuǎn)載于https://github.com/futurice/android-bes...
    Di_xin閱讀 680評論 0 12
  • 一次偶爾的機會聽到錦明老師的育兒課程!機緣巧合加入晨讀群周循。在群里默默聽著來自全國各地有能量的寶媽分享自己在這里的收...
    姜筍66閱讀 230評論 0 0