本文是Futurice公司的Android開發人員總結的最佳實踐,遵循這些準則能夠避免重複製造輪子。若是你對iOS或者Windows Phone開發感興趣,那麼也請看看iOS最佳實踐和Windows客戶端開發最佳實踐。java
將你的Android SDK放在你的home目錄或其餘應用程序無關的位置。 當安裝有些包含SDK的IDE的時候,可能會將SDK放在IDE同一目錄下,當你須要升級(或從新安裝)IDE或更換的IDE時,會很是麻煩。 此外,若果你的IDE是在普通用戶,不是在root下運行,還要避免吧SDK放到一下須要sudo權限的系統級別目錄下。android
你的默認編譯環境應該是Gradle. Ant 有不少限制,也很冗餘。使用Gradle,完成如下工做很方便:web
同時,Android Gradle插件做爲新標準的構建系統正在被Google積極的開發。shell
有兩種流行的結構:老的Ant & Eclipse ADT 工程結構,和新的Gradle & Android Studio 工程結構, 你應該選擇新的工程結構,若是你的工程還在使用老的結構,考慮放棄吧,將工程移植到新的結構。編程
老的結構後端
新的結構緩存
主要的區別在於:服務器
經常使用結構 參考網絡
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.+'}
Jackson 是一個將java對象轉換成JSON與JSON轉化java類的類庫。Gson 是解決這個問題的流行方案,然而咱們發現Jackson更高效,由於它支持替代的方法處理JSON:流、內存樹模型,和傳統JSON-POJO數據綁定。不過,請記住, Jsonkson庫比起GSON更大,因此根據你的狀況選擇,你可能選擇GSON來避免APP 65k個方法限制。其它選擇: Json-smart and 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不熟悉,只需參照如下開始學習吧:
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。
你能夠有一個沒有界面的fragment做爲Activity提供後臺工做。 進一步你可使用這個特性來建立一個fragment 包含改變其它fragment的邏輯 而不是把這個邏輯放在activity中。
你能夠選擇使用一個沒有UI界面的fragment來專門管理ActionBar,或者你能夠選擇使用在每一個Fragment中 添加它本身的action 來做爲父Activity的ActionBar.參考.
很不幸,咱們不建議普遍的使用嵌套的fragments,由於 有時會引發matryoshka bugs。咱們只有當它有意義(例如,在水平滑動的ViewPager在 像屏幕同樣fragment中)或者他的確是一個明智的選擇的時候才普遍的使用fragment。在一個架構級別,你的APP應該有一個頂級的activity來包含絕大部分業務相關的fragment。你也可能還有一些輔助的activity ,這些輔助的activity與主activity 通訊很簡單限制在這兩種方法 Intent.setData() 或 Intent.setAction()或相似的方法。
com.futurice.project ├─ network ├─ models ├─ managers ├─ utils ├─ fragments └─ views ├─ adapters ├─ actionbar ├─ widgets └─ notifications
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文件中。
例外有如下這些:
使用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文件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 你的佈局 和如何使用 `` 標籤.
當心關於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 或相似的異常, 即便編譯是沒有警告並運行成功。 這意味着如下兩種可能:
ProGuard 已經移除了類,枚舉,方法,成員變量或註解,考慮是不是必要的。
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。
在構建項目之初,發佈一個版本 來檢查ProGuard規則是否正確的保持了重要的部分。 同時不管什麼時候你添加了新的類庫,作一個發佈版本,同時apk在設備上跑起來測試一下。 不要等到你的app要發佈 "1.0"版本了才作版本發佈,那時候你可能會碰到好多意想不到的異常,須要一些時間去修復他們。
Tips每次發佈新版本都要寫 mapping.txt。每發佈一個版本,若是用戶遇到一個bug,同時提交了一個混淆過的堆棧跟蹤。 經過保留mapping.txt文件,來肯定你能夠調試的問題。
DexGuard 若果你須要核心工具來優化,和專門混淆的發佈代碼,考慮使用DexGuard, 一個商業軟件,ProGuard 也是有他們團隊開發的。 它會很容易將Dex文件分割,來解決65K個方法限制問題。
文章不易,若是你們喜歡這篇文章,或者對你有幫助但願你們多多點贊轉發關注哦。文章會持續更新的。絕對乾貨!!!