編輯:關於Android編程
原文鏈接:https://github.com/futurice/android-best-practices
本文是Futurice公司的Android開發人員總結的最佳實踐,遵循這些准則可以避免重復制造輪子。如果你對iOS或者WindowsPhone開發感興趣,那麼也請看看iOS最佳實踐和Windows客戶端開發最佳實踐。
第一版翻譯自:http://blog.csdn.net/asce1885 Android開發技術日新月異, Github上也有較大更新, 故對原文有增刪CSDN的markdown不支持頁面內跳轉, 概要裡的跳轉都是無效的. 如果您想看概要中每一條對應的詳細指導, 可以到https://github.com/oncealong/android-best-practices/blob/master/translations/Chinese/Readme.cn.new.md.
把你的Android SDK目錄放在電腦的主目錄或者其他跟IDE安裝目錄獨立的磁盤位置,某些IDE在安裝時就包含了Android SDK,而且可能把它放在跟IDE相同的目錄下。當你需要升級(或重新安裝)IDE,或者更換IDE時,這種做法是不好的。同樣要避免把Android SDK放在另外一個系統層級的目錄中,這樣當你的IDE在user模式下運行而不是root模式時,將需要sudo權限。
你的默認選擇應該是Gradle。相比之下,Ant限制更大而且使用起來更繁瑣。使用Gradle可以很簡單的實現:
1)將你的app編譯成不同的版本;
2)實現簡單的類似腳本的任務;
3)管理和下載第三方依賴項;
4)自定義密鑰庫;
5)其他
Google也在積極的開發Android的Gradle插件,以此作為新的標准編譯系統。
目前有兩個流行的選擇:以前的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
主要的區別在於新的結構明確的區分源碼集合(main
和androidTest
),這是從Gradle引入的概念。例如,你可以在源碼目錄src
中添加paid
和free
兩個子目錄,分別用來存放付費版和免費版app的源碼。
頂層的app
目錄有助於把你的app
和工程中會引用到的其他庫工程(例如library-foobar
)區分開。settings.gradle
文件中記錄了這些庫工程的引用,這樣app/build.gradle
就能夠引用到了。
一般結構:參見Google的安卓Gradle指南。
小任務:在Gradle中,我們使用tasks而不是腳本(shell,Python,Perl等使用腳本),詳細的內容可參見Gradle文檔。
密碼:發布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會自動導入這個文件,現在你可以在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。下載jar包並手動更新是很麻煩的,而這個問題Maven正好幫我們解決了,在Android Gradle構建中也建議這麼做。例子如下:
dependencies {
compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}
避免Maven的動態依賴解析 避免使用動態版本號, 如2.1.+
, 因為這可能會導致不同或者不穩定的構建, 也可能會導致不同構建中行為的微妙且難以追蹤的差異. 使用靜態版本號, 如2.1.1
有助於創建一個更穩定, 更可預測, 可重現問題的開發環境.
對不同的發布版本使用不同的打包名字 對debug構建類型使用applicationIdSuffix
, 從而可以在相同的設備上安裝debug和release的apk(如果你要自定義構建類型, 也這樣嘗試). 當app在商店發布後, 這會在app的生命周期中格外有價值.
android {
buildTypes {
debug {
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
release {
// ...
}
}
}
使用不同的圖標來區別不同的構建類型—例如使用不同的顏色, 或者用一個debug
標簽覆蓋圖標. Gradle使這些輕而易舉: 在默認的project結構上, 只要把debug
圖標放在app/src/debug/res
, 把release
圖標放在app/src/release/res
. 你也可以改變app名字來對應不同的構建類型, 也可以使用上文提到的versionName
.
使用任何可以良好應對工程結構的代碼編輯器。代碼編輯器是個人喜好的選擇,你需要做的是保證你所用的編輯器能夠和工程結構以及構建系統良好集成。
當下最受推崇的IDE是Android Studio,因為它是Google開發的,和Gradle耦合最好,默認使用最新的工程結構,已經處於穩定階段,是為Android開發量身定做的IDE。
當然你也可以使用Eclipse ADT,但你需要配置它才能使用Gradle,因為它默認使用的是舊的工程結構和使用Ant進行構建。你甚至可以使用類似Vim,Sublime Text,Emacs等文本編輯器,這種情況下你需要在命令行中使用Gradle和adb。如果你的Eclipse集成Gradle不可用,你的選擇是要麼使用命令行編譯或者把項目遷移到Android Studio中。Android Studio是最好的選擇,因為ADT插件已經被標記為過時了,也就是不會再作後續維護和更新了。
無論你使用哪種方式,需保證的是按照官方的推薦使用新的工程結構和Gradle來構建你的應用,並避免把你特定於編輯器的配置文件加入到版本控制系統中。例如要避免把Ant的build.xml文件添加到版本控制系統中。特別是如果你在Ant中更改了編譯配置,不要忘了同步更新build.gradle
文件。最後一點,要對其他開發人員友好,不要迫使他們修改他們所用編輯器的偏好設置。
Jackson是一個把Java對象轉換為JSON字符串或者把JSON字符串轉換成Java對象的Java函數庫。Gson也是解決這類問題很流行的選擇之一,但我們發現Jackson更加高性能,因為它支持多種可選的處理JSON的方式:流,內存樹模型和傳統的JSON-POJO數據綁定。盡管如此,Jackson是比Gson更大的函數庫,所以需要根據你項目的具體情況,你可能會選擇GSON來避免65k方法數限制。其他的選擇還有:Json-smart和Boon JSON。
網絡,緩存和圖像。向後端服務器發起網絡請求有很多經過實戰檢驗的解決方案,你應該使用這些解決方案而不是自己實現一個。使用Volley或者Retrofit吧!除了網絡請求,Volley還提供了幫助類用於加載和緩存圖像。如果你選擇Retrofit,那麼可以考慮使用Picasso作為加載和緩存圖像的函數庫,並結合OkHttp實現高效的HTTP請求。Retrofit,Picasso和OkHttp這三款函數庫都是同一家公司開發的,所以它們能夠很好的互補。Volley也能使用OkHttp來實現網絡連接。
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。
如果你之前沒有使用RxJava的經驗,那麼開始時可以僅在網絡請求API的響應處使用。如果有經驗了,可以將RxJava應用在簡單UI事件的處理,例如點擊事件或者搜索框中的輸入事件。如果你對自己的RxJava技能很自信而且想把RxJava應用到整個項目架構中,那麼在代碼難以理解的部分要編寫Javadocs。要記住對RxJava不熟悉的程序員可能在維護工程的初期會很痛苦。盡你所能幫助他們理解你的代碼和RxJava。
Retrolambda是兼容在Android中和JDK8之前的Java版本中使用Lambda表達式語法的一個Java函數庫。它幫助你的代碼保持緊湊和可讀,特別是當你使用函數式編程風格時,例如使用RxJava。要使用這個庫,需要安裝JDK8,在Android Studio工程結構對話框中設置SDK的位置,並設置JAVA8_HOME
和JAVA7_HOME
環境變量,然後在工程的build.gradle中設置如下:
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}
接著在各個模塊的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,可以參見下面的建議來開始:
1)任何只有一個函數的接口(Interface)是“lambda友好”的,可以被折疊為更緊湊的語法格式;
2)如果對參數或諸如此類的用法還存有懷疑的話,可以編寫一個普通的匿名內部類然後讓Android Studio幫你把它折疊成lambda表達式的形式。
要注意dex文件方法數限制的問題,避免使用太多的第三方函數庫。當Android應用打包成一個dex文件時,存在最多65536個引用方法數的限制問題。當超出這個限制時,你將在編譯階段看到fatal error。因此,應該使用盡可能少的第三方函數庫,並使用dex-method-counts工具來決定使用哪些函數庫的組合以避免不超過該限制。特別要避免使用Guava函數庫,因為它包含的方法數超過13k。(注:現在已經有了方法來解決這個問題, 比如multidex
)
[注:因為對activity和fragment優劣的比較一直在持續, 這裡和以前的版本改動較大, 但是這裡的結論仍不一定是最優結論. 讀者需要辯證的看待, 所以先給出最新的翻譯, 並把以前的翻譯保留, 以便讀者辯證思考.]
關於如何使用Activity和Fragment來最好的組織Android架構, 開發社區和Futurice公司的開發者都還沒有一致的看法. Squrare公司甚至有一個基於Views架構的庫, 這直接繞過了Fragments, 但是這種做法在開發社區不值得大范圍推廣使用.
由於Android API的歷史, Fragments可以粗略的認為用來組裝成UI. 或者說, Fragments 一般都是和UI相關的. Activities可以粗略的認為是控制器, 它們用於管理生命周期和狀態. 然而, 你可能會看到這些角色各有變種, activities可能用來承擔UI角色(提供畫面之間的轉換), fragments可能作為控制器單獨使用. 我們只能建議小心使用, 采取明智的決策, 因為fragments-only架構 或者 activities-only架構或者views-only架構都有缺點.下面是一些關於使用時小心什麼的建議, 但是還請你們持懷疑態度看待這些想法.
避免大范圍的使用嵌套fragments, 這會導致matryoshka bugs。只有在必要的時候(例如在Fragment屏幕中內嵌水平滑動的ViewPager)或者確實是明智的決定時才使用嵌套Fragments。 避免在activities中放太多代碼. 只要有可能, 盡量把它們作為輕量級容器, 主要用來負責生命周期的控制和其他重要的Android接口API. 優先選擇 single-fragment activity, 把UI相關的代碼放在activity的fragment中. 當你需要改變這個fragment讓它駐留在分頁布局或平板布局時, 這讓代碼可重用性提高. 避免使用沒有對應fragment的activity, 除非你知道你在做一個英明決策. 不要濫用Android系統級別的API, 如嚴重依賴Intent來進行app內部工作. 這樣可能會影響Android系統或其他的應用, 造成bug或者反應滯後. 例如, 眾所周知, 如果你的app使用Intent在不同包中進行通信, 當系統重啟後, 打開app可能會引發multi-second 滯後.
以前的看法:
在Android中實現UI界面默認應該選擇Fragments。Fragments是可復用用戶界面,能夠在你的應用中很好的組合。我們建議使用Fragments代替Activities來表示用戶界面,下面是幾點原因:
1)多窗口布局的解決方案。最初引入Fragment的原因是為了把手機應用適配到平板電腦屏幕上,這樣你可以在平板電腦屏幕上具有A和B兩個窗口,而到了手機屏幕上A或者B窗口獨占整個手機屏幕。如果你的應用在最開始的時候是基於Fragments實現的,那麼以後要適配到其他不同類型的屏幕上面會容易得多。
2)不同界面之間的通信。Android
API沒有提供在Activity之間傳遞復雜數據(例如某些Java對象)的正確方法。對於Fragments而已,你可以使用Activity的實例作為它的子Fragments之間通信的通道。雖然這種方法比Activities之間的通信更好,但你可能願意考慮Event
Bus框架,例如使用Otto或者green robot
EventBus作為更簡潔的方法。如果你想避免多添加一個函數庫的話,RxJava也可以用於實現Event Bus。
3)Fragments足夠通用,它不一定需要UI界面。你可以使用沒有UI界面的Fragment來完成Activity的後台工作。你可以進一步拓展這個功能,創建一個專門的後台Fragment來管理Activity的子Fragments之間的切換邏輯,這樣就不用在Activity中實現這些邏輯了。
4)在Fragments裡面也可以管理ActionBar。你可以選擇創建一個沒有UI界面的Fragment專門來管理ActionBar,或者選擇讓每個Fragments在宿主Activity的ActionBar中添加它們自己的action
items。更多請參考http://www.grokkingandroid.com/adding-action-items-from-within-fragments/。
我們建議不要大量的使用嵌套Fragments,這會導致matryoshka
bugs。只有在必要的時候(例如在Fragment屏幕中內嵌水平滑動的ViewPager)或者確實是明智的決定時才使用嵌套Fragments。
從軟件架構的層面看,你的app應該具有一個包含大部分業務相關fragments的頂級activity。當然你也可以有其他輔助activities,這些activities與主activity的具有簡單的數據通信,例如通過Intent.setData()或者Intent.setAction()等等。
Android應用的Java包結構大致類似MVC模式。在Android中aspx?p=2126865">Fragment和Activity實際上是controller類,另一方面,它們顯然也是用戶界面的一部分,因此也是View類。
因此,很難嚴格界定Fragments(或者Activities)是controllers類還是views類。最好把Fragments類單獨放在fragments包裡面。如果你遵循前面段落的建議的話(只有一個主Activity),Activities可以放在頂層的包裡面。如果你計劃會存在多個activities,那麼就將Activity放在單獨的activities包裡面。
另一方面,整個包結構看起來很像經典的MVC框架,models包目錄存放網絡請求API響應經過JSON解析器填充後得到的POJOs對象。views包目錄存放自定義的views,notifications,action bar views,widgets等等。Adapters處於灰色地帶,介於data和views之間,然而,它們一般需要通過getView()函數導出視圖,所以你可以在views包中增加adapters子包。
有的controller類是應用范圍的並和Android系統緊密關聯,它們可以放在managers包裡面。其他的數據處理類,例如“DateUtils”,可以放在utils包裡面。與服務器端響應交互的類放在network包裡面。
總之,整個包結構從服務器端到用戶界面劃分如下所示:
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
命名。遵循以類型作為前綴命名的慣例,即type_foo_bar.xml
。例子如下:fragment_contact_details.xml
,view_primary_button.xml
,activity_main.xml
。
組織布局XMLs。如果你對如何格式化布局XML文件不大清楚的話,那麼下面的慣例或許可以幫你:
1)每行一個屬性,縮進四個空格
2)android:id
始終作為第一個屬性
3)android:layout_****
屬性放在頭部
4)style
屬性放在尾部
5)結束標簽/>
放在單獨一行,便於新增屬性或者重新排列屬性
6)考慮使用Android Studio中的Designtime attributes,而不使用android:text硬編碼。
<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"> </include></textview></linearlayout> " data-snippet-id="ext.3ac53dfc1835d017988fcb6c915c0df3" data-snippet-saved="false" data-codota-status="done"><code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E--> <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"> <include layout="@layout/reusable_part"> </include></textview></linearlayout> </code>
一般來說android:layout_****
屬性應該定義在XML布局文件中,而其他的android:****
屬性應該放在樣式xml文件中。這條法則也有例外,但一般情況是這樣的。這種做法是為了在布局文件中只保留布局屬性(位置,留白,大小)和內容屬性,而其他外觀細節(顏色,填充,字體)放到樣式文件中。
例外的有:
- android:id
必須放在layout文件中
- 在LinearLayout
中的android:orientation
一般放在layout文件中更有意思
- android:text
必須放在layout文件中,因為它定義了內容
- 有時創建通用的style文件並定義android:layout_width
和android:layout_height
屬性更有意義,但默認情況下這兩個屬性應該放在layout文件中。
使用styles。幾乎所有工程都需要正確的使用styles,因為它是讓view具有相同外觀的常見的方法。你的應用的文本內容應該至少具有一個公用的樣式,例如:
- @dimen/font_normal
- @color/basic_black
" data-snippet-id="ext.0ddc269f69449fd605e55ef7a9e0cca4" data-snippet-saved="false" data-codota-status="done">
用在TextView上面如下:
" data-snippet-id="ext.7be271f4e6db33ede0faf99acda3f7d1" data-snippet-saved="false" data-codota-status="done">
你可能需要對buttons做類似的工作,但別就此停住。繼續把相關的和重復的android:****
屬性分組到公用的style文件中。
把一個大的style文件細分成多個文件。你沒有必要維護單獨一個styles.xml
文件,Android SDK能很好的支持其他文件。styles文件的名字並不一定要是styles.xml
,起作用的是xml文件裡面的
翻譯自:https://code.facebook.com/posts/366199913563917 快速有效的展示圖片對Facebook Android客戶
概述:Android事件構成:在Android中,事件主要包括點按、長按、拖拽、滑動等,點按又包括單擊和雙擊,另外還包括單指操作和多指操作。所有這些都構成了Android
Android廣播BroadcastReceiver Android 系統裡定義了各種各樣的廣播,如電池的使用狀態,電話的接收和短信的接收,開機啟動都會產生一個
1.Handler是什麼?Handler是Android給我們提供的一套更新UI的機制,也封裝了一套消息處理的機制,可以發送消息,也可以通過它處理消息。eg:Handle