Android 開發(fā)最佳實踐

Futurice公司Android開發(fā)者中學(xué)到的經(jīng)驗蹬挺。
遵循以下準(zhǔn)則瓣俯,避免重復(fù)發(fā)明輪子闰非。若您對開發(fā)iOS或Windows Phone 有興趣剩盒,
請看iOS Good PracticesWindows client Good Practices 這兩篇文章。

摘要

  • 使用 Gradle 和它推薦的工程結(jié)構(gòu)
  • 把密碼和敏感數(shù)據(jù)放在gradle.properties
  • 不要自己寫 HTTP 客戶端,使用Volley或OkHttp庫
  • 使用Jackson庫解析JSON數(shù)據(jù)
  • 避免使用Guava同時使用一些類庫來避免65k method limit(一個Android程序中最多能執(zhí)行65536個方法)
  • 使用 Fragments來呈現(xiàn)UI視圖
  • 使用 Activities 只是為了管理 Fragments
  • Layout 布局是 XMLs代碼台夺,組織好它們
  • 在layoutout XMLs布局時径玖,使用styles文件來避免使用重復(fù)的屬性
  • 使用多個style文件來避免單一的一個大style文件
  • 保持你的colors.xml 簡短DRY(不要重復(fù)自己),只是定義調(diào)色板
  • 總是使用dimens.xml DRY(不要重復(fù)自己)颤介,定義通用常數(shù)
  • 不要做一個深層次的ViewGroup
  • 在使用WebViews時避免在客戶端做處理梳星,當(dāng)心內(nèi)存泄露
  • 使用Robolectric單元測試,Robotium 做UI測試
  • 使用Genymotion 作為你的模擬器
  • 總是使用ProGuard 和 DexGuard混淆來項目

Android SDK

將你的Android SDK放在你的home目錄或其他應(yīng)用程序無關(guān)的位置滚朵。
當(dāng)安裝有些包含SDK的IDE的時候冤灾,可能會將SDK放在IDE同一目錄下,當(dāng)你需要升級(或重新安裝)IDE或更換的IDE時辕近,會非常麻煩韵吨。
此外,若果你的IDE是在普通用戶移宅,不是在root下運行归粉,還要避免吧SDK放到一下需要sudo權(quán)限的系統(tǒng)級別目錄下。

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

你的默認(rèn)編譯環(huán)境應(yīng)該是Gradle.
Ant 有很多限制漏峰,也很冗余糠悼。使用Gradle,完成以下工作很方便:

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

同時浅乔,Android Gradle插件作為新標(biāo)準(zhǔn)的構(gòu)建系統(tǒng)正在被Google積極的開發(fā)倔喂。

工程結(jié)構(gòu)

有兩種流行的結(jié)構(gòu):老的Ant & Eclipse ADT 工程結(jié)構(gòu),和新的Gradle & Android Studio 工程結(jié)構(gòu)靖苇,
你應(yīng)該選擇新的工程結(jié)構(gòu)席噩,如果你的工程還在使用老的結(jié)構(gòu),考慮放棄吧顾复,將工程移植到新的結(jié)構(gòu)班挖。

老的結(jié)構(gòu):

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

新的結(jié)構(gòu)

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

主要的區(qū)別在于鲁捏,新的結(jié)構(gòu)明確的分開了'source sets' (main,androidTest)芯砸,Gradle的一個理念萧芙。
你可以做到,例如假丧,添加源組‘paid’和‘free’在src中双揪,這將成為您的應(yīng)用程序的付費和免費的兩種模式的源代碼。

你的項目引用第三方項目庫時(例如包帚,library-foobar)渔期,擁有一個頂級包名app從第三方庫項目區(qū)分你的應(yīng)用程序是非常有用的。
然后settings.gradle不斷引用這些庫項目渴邦,其中app/build.gradle可以引用疯趟。

Gradle 配置

常用結(jié)構(gòu) 參考Google's guide on Gradle for Android

小任務(wù) 除了(shell, Python, Perl, etc)這些腳本語言,你也可以使用Gradle 制作任務(wù)谋梭。
更多信息請參考Gradle's documentation信峻。

密碼 在做版本release時你app的 build.gradle你需要定義 signingConfigs.此時你應(yīng)該避免以下內(nèi)容:

不要做這個 . 這會出現(xiàn)在版本控制中。

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

而是瓮床,建立一個不加入版本控制系統(tǒng)的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 依賴方案代替使用導(dǎo)入jar包方案 如果在你的項目中你明確使用率
jar文件踢步,那么它們可能成為永久的版本,如2.1.1.下載jar包更新他們是很繁瑣的丑掺,
這個問題Maven很好的解決了获印,這在Android Gradle構(gòu)建中也是推薦的方法。你可
以指定版本的一個范圍吼鱼,如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集成開發(fā)環(huán)境和文本編輯器

無論使用什么編輯器,一定要構(gòu)建一個良好的工程結(jié)構(gòu) 編輯器每個人都有自己的
選擇菇肃,讓你的編輯器根據(jù)工程結(jié)構(gòu)和構(gòu)建系統(tǒng)運作地粪,那是你自己的責(zé)任。

當(dāng)下首推Android Studio,因為他是由谷歌開發(fā)琐谤,最接近Gradle蟆技,默認(rèn)使用最新的工程結(jié)構(gòu),已經(jīng)到beta階段
(目前已經(jīng)有release 1.0了)斗忌,它就是為Android開發(fā)定制的质礼。

你也可以使用Eclipse ADT ,但是你需要對它進(jìn)行配置织阳,因為它使用了舊的工程結(jié)構(gòu)
和Ant作為構(gòu)建系統(tǒng)眶蕉。你甚至可以使用純文版編輯器如Vim,Sublime Text唧躲,或者Emacs造挽。如果那樣的話碱璃,你需要使用Gardle和adb命令行。如果使用Eclipse集成Gradle
不適合你饭入,你只是使用命令行構(gòu)建工程嵌器,或遷移到Android Studio中來吧。

無論你使用何種開發(fā)工具谐丢,只要確保Gradle和新的項目結(jié)構(gòu)保持官方的方式構(gòu)建應(yīng)用程序爽航,避免你的編輯器配置文件加入到版本控制。例如乾忱,避免加入Ant build.xml文件讥珍。
特別如果你改變Ant的配置,不要忘記保持build.gradle是最新和起作用的窄瘟。同時串述,善待其他開發(fā)者,不要強制改變他們的開發(fā)工具和偏好寞肖。

類庫

Jackson 是一個將java對象轉(zhuǎn)換成JSON與JSON轉(zhuǎn)化java類的類庫纲酗。Gson
是解決這個問題的流行方案,然而我們發(fā)現(xiàn)Jackson更高效,因為它支持替代的方法處理JSON:流新蟆、內(nèi)存樹模型,和傳統(tǒng)JSON-POJO數(shù)據(jù)綁定觅赊。不過,請記住琼稻,
Jsonkson庫比起GSON更大吮螺,所以根據(jù)你的情況選擇,你可能選擇GSON來避免APP 65k個方法限制帕翻。其它選擇: Json-smart and Boon JSON

網(wǎng)絡(luò)請求鸠补,緩存,圖片 執(zhí)行請求后端服務(wù)器嘀掸,有幾種交互的解決方案紫岩,你應(yīng)該考慮實現(xiàn)你自己的網(wǎng)絡(luò)客戶端。使用 Volley
Retrofit睬塌。Volley 同時提供圖片緩存類泉蝌。若果你選擇使用Retrofit,那么考慮使用Picasso
來加載圖片和緩存,同時使用OkHttp作為高效的網(wǎng)絡(luò)請求揩晴。Retrofit勋陪,Picasso和OkHttp都是有同一家公司開發(fā)(注:
是由Square 公司開發(fā)),所以它們能很好的在一起運行硫兰。OkHttp 同樣可以和Volley在一起使用 Volley.

RxJava 是函數(shù)式反應(yīng)性的一個類庫诅愚,換句話說,能處理異步的事件劫映。
這是一個強大的和有前途的模式违孝,同時也可能會造成混淆壕曼,因為它是如此的不同。
我們建議在使用這個庫架構(gòu)整個應(yīng)用程序之前要謹(jǐn)慎考慮等浊。
有一些項目是使用RxJava完成的,如果你需要幫助可以跟這些人取得聯(lián)系:
Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen.
我們也寫了一些博客:
[1], [2],
[3],
[4].

如若你之前有使用過Rx的經(jīng)歷摹蘑,開始從API響應(yīng)應(yīng)用它筹燕。
另外,從簡單的UI事件處理開始運用衅鹿,如單擊事件或在搜索欄輸入事件撒踪。
若對你的Rx技術(shù)有信心,同時想要將它應(yīng)用到你的整體架構(gòu)中大渤,那么請在復(fù)雜的部分寫好Javadocs文檔制妄。
請記住其他不熟悉RxJava的開發(fā)人員,可能會非常難理解整個項目泵三。
盡你的的全力幫助他們理解你的代碼和Rx耕捞。

Retrolambda 是一個在Android和預(yù)JDK8平臺上的使用Lambda表達(dá)式語法的Java類庫。
它有助于保持你代碼的緊湊性和可讀性烫幕,特別當(dāng)你使用如RxJava函數(shù)風(fēng)格編程時俺抽。
使用它時先安裝JDK8,在Android Studio工程結(jié)構(gòu)對話框中把它設(shè)置成為SDK路徑较曼,同時設(shè)置JAVA8_HOMEJAVA7_HOME環(huán)境變量磷斧,
然后在工程根目錄下配置 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不熟悉捷犹,只需參照以下開始學(xué)習(xí)吧:

  • 任何只包含一個接口的方法都是"lambda friendly"同時代碼可以被折疊成更緊湊的語法
  • 如果對參數(shù)或類似有疑問弛饭,就寫一個普通的匿名內(nèi)部類,然后讓Android Status為你生成一個lambda萍歉。

當(dāng)心dex方法數(shù)限制侣颂,同時避免使用過多的類庫 Android apps卒落,當(dāng)打包成一個dex文件時蜈抓,有一個65535個應(yīng)用方法強硬限制[1] [2] [3]囚巴。
當(dāng)你突破65k限制之后你會看到一個致命錯誤伦籍。因此坐慰,使用一個正常范圍的類庫文件焙贷,同時使用dex-method-counts
工具來決定哪些類庫可以再65k限制之下使用镜廉,特別的避免使用Guava類庫贺喝,因為它包含超過13k個方法斗幼。

Activities and Fragments

Fragments應(yīng)該作為你實現(xiàn)UI界面默認(rèn)選擇澎蛛。你可以重復(fù)使用Fragments用戶接口來
組合成你的應(yīng)用。我們強烈推薦使用Fragments而不是activity來呈現(xiàn)UI界面蜕窿,理由如下:

  • 提供多窗格布局解決方案 Fragments 的引入主要將手機應(yīng)用延伸到平板電腦谋逻,所以在平板電腦上你可能有A呆馁、B兩個窗格,但是在手機應(yīng)用上A毁兆、B可能分別充滿
    整個屏幕浙滤。如果你的應(yīng)用在最初就使用了fragments,那么以后將你的應(yīng)用適配到其他不同尺寸屏幕就會非常簡單气堕。

  • 屏幕間數(shù)據(jù)通信 從一個Activity發(fā)送復(fù)雜數(shù)據(jù)(例如Java對象)到另外一個Activity纺腊,Android的API并沒有提供合適的方法。不過使用Fragment茎芭,你可以使用
    一個activity實例作為這個activity子fragments的通信通道揖膜。即使這樣比Activity與Activity間的通信好,你也想考慮使用Event Bus架構(gòu)梅桩,使用如
    Otto 或者 greenrobot EventBus作為更簡潔的實現(xiàn)壹粟。
    如果你希望避免添加另外一個類庫,RxJava同樣可以實現(xiàn)一個Event Bus宿百。

  • Fragments 一般通用的不只有UI 你可以有一個沒有界面的fragment作為Activity提供后臺工作趁仙。
    進(jìn)一步你可以使用這個特性來創(chuàng)建一個fragment 包含改變其它fragment的邏輯
    而不是把這個邏輯放在activity中。

  • 甚至ActionBar 都可以使用內(nèi)部fragment來管理 你可以選擇使用一個沒有UI界面的fragment來專門管理ActionBar,或者你可以選擇使用在每個Fragment中
    添加它自己的action 來作為父Activity的ActionBar.參考.

很不幸垦页,我們不建議廣泛的使用嵌套的fragments幸撕,因為
有時會引起matryoshka bugs。我們只有當(dāng)它有意義(例如外臂,在水平滑動的ViewPager在
像屏幕一樣fragment中)或者他的確是一個明智的選擇的時候才廣泛的使用fragment坐儿。

在一個架構(gòu)級別,你的APP應(yīng)該有一個頂級的activity來包含絕大部分業(yè)務(wù)相關(guān)的fragment宋光。你也可能還有一些輔助的activity 貌矿,這些輔助的activity與主activity
通信很簡單限制在這兩種方法
Intent.setData()Intent.setAction()或類似的方法。

Java 包結(jié)構(gòu)

Android 應(yīng)用程序在架構(gòu)上大致是Java中的Model-View-Controller結(jié)構(gòu)罪佳。
在Android 中 Fragment和Activity通常上是控制器類(http://www.informit.com/articles/article.aspx?p=2126865).
換句話說逛漫,他們是用戶接口的部分,同樣也是Views視圖的部分赘艳。

正是因為如此酌毡,才很難嚴(yán)格的將fragments (或者 activities) 嚴(yán)格的劃分成 控制器controlloers還是視圖 views。
最還是將它們放在自己單獨的 fragments 包中蕾管。只要你遵循之前提到的建議枷踏,Activities 則可以放在頂級目錄下。
若果你規(guī)劃有2到3個以上的activity掰曾,那么還是同樣新建一個activities包吧旭蠕。

然而,這種架構(gòu)可以看做是另一種形式的MVC,
包含要被解析API響應(yīng)的JSON數(shù)據(jù)掏熬,來填充的POJO的models包中佑稠。
和一個views包來包含你的自定義視圖、通知旗芬、導(dǎo)航視圖舌胶,widgets等等。
適配器Adapter是在數(shù)據(jù)和視圖之間疮丛。然而他們通常需要通過getView()方法來導(dǎo)出一些視圖幔嫂,
所以你可以將adapters包放在views包里面。

一些控制器角色的類是應(yīng)用程序級別的这刷,同時是接近系統(tǒng)的。
這些類放在managers包下面娩井。
一些繁雜的數(shù)據(jù)處理類暇屋,比如說"DateUtils",放在utils包下面。
與后端交互負(fù)責(zé)網(wǎng)絡(luò)處理類洞辣,放在network包下面咐刨。

總而言之,以最接近用戶而不是最接近后端去安排他們扬霜。

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

資源文件 Resources

  • 命名 遵循前綴表明類型的習(xí)慣定鸟,形如type_foo_bar.xml。例如:fragment_contact_details.xml,view_primary_button.xml,activity_main.xml.

組織布局文件 若果你不確定如何排版一個布局文件著瓶,遵循一下規(guī)則可能會有幫助联予。

  • 每一個屬性一行,縮進(jìn)4個空格
  • android:id 總是作為第一個屬性
  • android:layout_**** 屬性在上邊
  • style 屬性在底部
  • 關(guān)閉標(biāo)簽/>單獨起一行材原,有助于調(diào)整和添加新的屬性
  • 考慮使用Designtime attributes 設(shè)計時布局屬性沸久,Android Studio已經(jīng)提供支持,而不是硬編碼android:text
    (譯者注:墻內(nèi)也可以參考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>

作為一個經(jīng)驗法則,android:layout_****屬性應(yīng)該在 layout XML 中定義,同時其它屬性android:**** 應(yīng)放在 styler XML中卷胯。此規(guī)則也有例外,不過大體工作
的很好威酒。這個思想整體是保持layout屬性(positioning, margin, sizing) 和content屬性在布局文件中窑睁,同時將所有的外觀細(xì)節(jié)屬性(colors, padding, font)放
在style文件中。

例外有以下這些:

  • android:id 明顯應(yīng)該在layout文件中
  • layout文件中android:orientation對于一個LinearLayout布局通常更有意義
  • android:text 由于是定義內(nèi)容葵孤,應(yīng)該放在layout文件中
  • 有時候?qū)?code>android:layout_width 和 android:layout_height屬性放到一個style中作為一個通用的風(fēng)格中更有意義担钮,但是默認(rèn)情況下這些應(yīng)該放到layout文件中。

使用styles 幾乎每個項目都需要適當(dāng)?shù)氖褂胹tyle文件尤仍,因為對于一個視圖來說有一個重復(fù)的外觀是很常見的裳朋。
在應(yīng)用中對于大多數(shù)文本內(nèi)容,最起碼你應(yīng)該有一個通用的style文件,例如:

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

應(yīng)用到TextView 中:

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

你或許需要為按鈕控件做同樣的事情鲤嫡,不要停止在那里送挑。將一組相關(guān)的和重復(fù)android:****的屬性放到一個通用的style中。

將一個大的style文件分割成多個文件 你可以有多個styles.xml 文件暖眼。Android SDK支持其它文件惕耕,styles這個文件名稱并沒有作用,起作用的是在文件
里xml的<style>標(biāo)簽诫肠。因此你可以有多個style文件styles.xml,style_home.xml,style_item_details.xml,styles_forms.xml司澎。
不用于資源文件路徑需要為系統(tǒng)構(gòu)建起的有意義,在res/values目錄下的文件可以任意命名栋豫。

colors.xml是一個調(diào)色板 在你的colors.xml文件中應(yīng)該只是映射顏色的名稱一個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>

使用這種格式蛤铜,你會非常容易的開始重復(fù)定義RGBA值,這使如果需要改變基本色變的很復(fù)雜丛肢。同時围肥,這些定義是跟一些環(huán)境關(guān)聯(lián)起來的,如button或者comment,
應(yīng)該放到一個按鈕風(fēng)格中蜂怎,而不是在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>

向應(yīng)用設(shè)計者那里要這個調(diào)色板杠步,名稱不需要跟"green", "blue", 等等相同氢伟。
"brand_primary", "brand_secondary", "brand_negative" 這樣的名字也是完全可以接受的。
像這樣規(guī)范的顏色很容易修改或重構(gòu)幽歼,會使應(yīng)用一共使用了多少種不同的顏色變得非常清晰。
通常一個具有審美價值的UI來說试躏,減少使用顏色的種類是非常重要的。

像對待colors.xml一樣對待dimens.xml文件 與定義顏色調(diào)色板一樣颠蕴,你同時也應(yīng)該定義一個空隙間隔和字體大小的“調(diào)色板”。
一個好的例子椅您,如下所示:

<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 時掀泳,你應(yīng)該使用spacing_****尺寸格式來布局,而不是像對待String字符串一樣直接寫值员舵。
這樣寫會非常有感覺脑沿,會使組織和改變風(fēng)格或布局是非常容易。

避免深層次的視圖結(jié)構(gòu) 有時候為了擺放一個視圖马僻,你可能嘗試添加另一個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當(dāng)中措近,也是可能會發(fā)生的。

可能會導(dǎo)致一系列的問題女淑。你可能會遇到性能問題瞭郑,因為處理起需要處理一個復(fù)雜的UI樹結(jié)構(gòu)。
還可能會導(dǎo)致以下更嚴(yán)重的問題StackOverflowError.

因此盡量保持你的視圖tree:學(xué)習(xí)如何使用RelativeLayout,
如何 optimize 你的布局 和如何使用
<merge> 標(biāo)簽.

小心關(guān)于WebViews的問題. 如果你必須顯示一個web視圖鸭你,
比如說對于一個新聞文章屈张,避免做客戶端處理HTML的工作,
最好讓后端工程師協(xié)助苇本,讓他返回一個 "" HTML袜茧。
WebViews 也能導(dǎo)致內(nèi)存泄露
當(dāng)保持引他們的Activity菜拓,而不是被綁定到ApplicationContext中的時候瓣窄。
當(dāng)使用簡單的文字或按鈕時,避免使用WebView纳鼎,這時使用TextView或Buttons更好俺夕。

測試框架

Android SDK的測試框架還處于初級階段,特別是關(guān)于UI測試方面贱鄙。Android Gradle
目前實現(xiàn)了一個叫connectedAndroidTest的測試,
使用一個JUnit 為Android提供的擴展插件 extension of JUnit with helpers for Android.可以跑你生成的JUnit測試映九,

只當(dāng)做單元測試時使用 Robolectric 件甥,views 不用
它是一個最求提供"不連接設(shè)備的"為了加速開發(fā)的測試引有,
非常時候做 models 和 view models 的單元測試。
然而曾我,使用Robolectric測試時不精確的斋荞,也不完全對UI測試平酿。
當(dāng)你對有關(guān)動畫的UI元素、對話框等幸逆,測試時會有問題还绘,
這主要是因為你是在 “在黑暗中工作”(在沒有可控的界面情況下測試)

**Robotium 使寫UI測試非常簡單拍顷。
** 對于UI測試你不需 Robotium 跑與設(shè)備連接的測試。
但它可能會對你有益踏揣,是因為它有許多來幫助類的獲得和分析視圖捞稿,控制屏幕。
測試用例看起來像這樣簡單:

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

模擬器

如果你全職開發(fā)Android App,那么買一個Genymotion emulatorlicense吧铃辖。
Genymotion 模擬器運行更快的秒幀的速度仁卷,比起典型的AVD模擬器锦积。他有演示你APP的工具丰介,高質(zhì)量的模擬網(wǎng)絡(luò)連接哮幢,GPS位置橙垢,等等柜某。它同時還有理想的連接測試。
你若涉及適配使用很多不同的設(shè)備翰绊,買一個Genymotion 版權(quán)是比你買很多真設(shè)備便宜多的辞做。

注意:Genymotion模擬器沒有裝載所有的Google服務(wù),如Google Play Store和Maps童叠。你也可能需
要測試Samsung指定的API,若這樣的話你還是需要購買一個真實的Samsung設(shè)備杜秸。

混淆配置

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

你是否使用ProGuard取決你項目的配置惶傻,當(dāng)你構(gòu)建一個release版本的apk時银室,通常你應(yīng)該配置gradle文件蜈敢。

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

為了決定哪些代碼應(yīng)該被保留,哪些代碼應(yīng)該被混淆辐宾,你不得不指定一個或多個實體類在你的代碼中。
這些實體應(yīng)該是指定的類包含main方法誉察,applets持偏,midlets,activities卿叽,等等考婴。
Android framework 使用一個默認(rèn)的配置文件沥阱,可以在SDK_HOME/tools/proguard/proguard-android.txt
目錄下找到屁使。自定義的工程指定的 project-specific 混淆規(guī)則蛮寂,如在my-project/app/proguard-rules.pro中定義,
會被添加到默認(rèn)的配置中范抓。

關(guān)于 ProGuard 一個普遍的問題匕垫,是看應(yīng)用程序是否崩潰并報ClassNotFoundException 或者 NoSuchFieldException 或類似的異常,
即使編譯是沒有警告并運行成功偶惠。
這意味著以下兩種可能:

  1. ProGuard 已經(jīng)移除了類忽孽,枚舉,方法出革,成員變量或注解蹋盆,考慮是否是必要的。
  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 中的一些例子糜颠。
更多例子請參考Proguard顶瞒。

在構(gòu)建項目之初,發(fā)布一個版本 來檢查ProGuard規(guī)則是否正確的保持了重要的部分匀归。
同時無論何時你添加了新的類庫袱贮,做一個發(fā)布版本攒巍,同時apk在設(shè)備上跑起來測試一下闻坚。
不要等到你的app要發(fā)布 "1.0"版本了才做版本發(fā)布鲤氢,那時候你可能會碰到好多意想不到的異常卷玉,需要一些時間去修復(fù)他們。

Tips每次發(fā)布新版本都要寫 mapping.txt寝并。每發(fā)布一個版本衬潦,如果用戶遇到一個bug镀岛,同時提交了一個混淆過的堆棧跟蹤。
通過保留mapping.txt文件走越,來確定你可以調(diào)試的問題旨指。

DexGuard 若果你需要核心工具來優(yōu)化,和專門混淆的發(fā)布代碼算柳,考慮使用DexGuard,
一個商業(yè)軟件蔗蹋,ProGuard 也是有他們團隊開發(fā)的。
它會很容易將Dex文件分割皂吮,來解決65K個方法限制問題。

致謝

感謝Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori M?ntyl?, Mark Voit, Andre Medeiros, Paul Houghton 這些人和Futurice 開發(fā)者分享他們的Android開發(fā)經(jīng)驗艺挪。

License

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

Translation

Translated to Chinese by andyiac

本文為轉(zhuǎn)載,原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疆瑰,隨后出現(xiàn)的幾起案子乃摹,更是在濱河造成了極大的恐慌,老刑警劉巖掰读,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拢肆,居然都是意外死亡支示,警方通過查閱死者的電腦和手機颂鸿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人闲擦,你說我怎么就攤上這事≌廾眨” “怎么了俺榆?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長装哆。 經(jīng)常有香客問我罐脊,道長,這世上最難降的妖魔是什么蜕琴? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任萍桌,我火速辦了婚禮凌简,結(jié)果婚禮上裳食,老公的妹妹穿的比我還像新娘救氯。我一直安慰自己享扔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著令宿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天凳枝,我揣著相機與錄音,去河邊找鬼捶箱。 笑死,一個胖子當(dāng)著我的面吹牛看蚜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼查坪,長吁一口氣:“原來是場噩夢啊……” “哼遥昧!你這毒婦竟也來了威创?” 一聲冷哼從身側(cè)響起截碴,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后曹阔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彪薛,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡牵舵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年笨鸡,在試婚紗的時候發(fā)現(xiàn)自己被綠了辱挥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡庆冕,死狀恐怖惭载,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤商叹,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布贷币,位于F島的核電站,受9級特大地震影響抽活,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锰什,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一下硕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汁胆,春花似錦梭姓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铸题,卻和暖如春铡恕,著一層夾襖步出監(jiān)牢的瞬間琢感,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工探熔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驹针,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓诀艰,卻偏偏與公主長得像柬甥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子其垄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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