標籤:

數庫科技 Android 開發準則

該份開發準則來源並作用於數庫科技 Android 開發組,轉載請標明出處。歡迎對 使用 Kotlin 進行工作 有興趣的開發者聯繫我並投遞簡歷(也招 iOS、Web 前後端、設計)~文章內容首發於我的 Git Blog(知乎的排版實在太爛了,要仔細看的話還是建議到 Github 上看 =_=),並將長期在 Github 上進行維護。

總覽

設計

  • 定義好調色板,所有顏色 只能 從調色板中獲取,任何地方都應該避免硬編碼
  • 圖標可以考慮使用流行圖標字體庫(Fontawesome)
  • 開發前對一遍設計稿,定好所有 Dimen,盡量使用 Dimen 板
  • Multiple State(多狀態) Drawable 命名規則:android-selector-chapek
  • 設計規範越完整,開發越容易工作

開發

  • 必須寫注釋(行內注釋,函數注釋以及類注釋等),Doc Gen 選用 Dodoka
  • 善用 TODO,FIXME 進行標註
  • 必須寫單元測試(V/M 層都需要)
  • 使用 CI(持續集成)進行遠程構建
  • 使用 Lint 工具進行代碼靜態檢查
  • 收集各種常用 Lib (log, bugly, ...)
  • 可以添加多一層 Lib 層,用來對大多數第三方庫進行一層包裹(Wrap),方便日後更換或拓展
  • 所有基於事件響應的場景盡量使用 Rx 來實現,包括 View 的事件響應(可參考 ReactiveAndroid)
  • 所有調試用的 Log 請用使用 Debug 作為 Flag 進行輸出,Release 環境下必須使用混淆去掉所有 Log 的代碼
  • 上架前必須進行 混淆 和 簽名
  • 使用 Redex 等工具對 Dex 文件進行優化(也可使用 (redex-plugin)[https://github.com/timmutton/redex-plugin])
  • 使用 Nimbledroid 進行應用性能分析
  • 適當使用依賴注入(常用的模塊,需要單元測試的模塊)
  • 使用 Fragment 來構建頁面內容,使用 Activity 來管理 Fragment
  • 盡量使用 Anko DSL 來創建視圖

架構

MVP,Flux/Redux。請參考 Kotgo。

層次流程

  • View -> Model -> Presenter:View 和高復用性的 Model 同時開發,Presenter 最後開發。

  • 前期 View 層開發需要用到的數據全部使用 ViewObject,需要什麼屬性就定義什麼屬性,以後在 Presenter 層進行DO 到 VO 再到 View 的 Convert(轉換)過程。(VO 中可以使用 data: Any 屬性攜帶 DO)

  • DO 到 VO 的轉化過程請不要在 UI 線程進行操作。(可以在 Presenter 中使用 Rx 的 Map 操作在非主線程調度器上進行轉換)

  • 所有 VO,DO 只能保存在 Presenter 層內,View 層最多只能保存 VO 的引用!(另外要注意,Adapter 應當放在 Presenter 層內)

  • View 層不能接觸 Model 層的任何數據和介面!

  • 頁面跳轉 放到 Presenter 層中。

  • View 和 Presenter 之間是雙向依賴,所以通過介面解藕,便於進行 UI Mock 測試,而 Presenter 和 Model 是單向依賴,可以直接編寫單元測試來測試 Model。

Flux 的一些思想

  • FP 的思想很適合前端:Rx 在 Android 領域的火爆驗證了這一點(對事件或數據的流加工)
  • Pure function:Function 不影響外部變數(不產生副作用),且給定輸入,輸出不變。
  • Map,Reduce:對數據的流處理,任意流都可以通過 Map&Reduce 加工成任意流。

Git 協同守則

  • 拉取 dev 分支到本地 Liveneeq 文件夾

mkdir Liveneeqncd Liveneeqngit initngit remote add -t dev -f origin git@git.thecampus.cc:onecampus/liveneeq-android.gitngit checkout -b dev origin/devn

  • 每個人建立帶下劃線的自己全名的分支,例如 _yangfan

git checkout -b _yangfann

  • 在該分支上進行開發,定期進行 Commit(可使用 tmp 前綴來表示臨時提交),確保代碼在雲端

git add .ngit commit -m "tmp 3/18 ***"ngit push origin _yangfannngit add .ngit commit -m "tmp 3/19 ***"ngit push origin _yangfann

  • 完成階段性功能或頁面後,使用 rebase 或者 reset 重建 Commit 歷史,確保所有 tmp commit 被合併刪除

git rebase -i <COMMIT_HASH>n# ...ngit rebase --continuen

  • 需要提交到 dev 分支時,需要針對 dev 在個人分支上進行 Rebase 操作,並處理衝突

git fetchngit rebase origin/devn# ...n

  • rebase 完成後 在本機進行構建和測試,測試通過後使用 -f 參數強制 Push 到遠程分支

git push -f origin _yangfann

  • 在 Gitlab 上提交 Merge Request(/Pull Request) 到 dev 分支,等待 Master 進行 Code Review

Notice

  • 每次 Commit 要保證粒度足夠細,包含的更改和描述一致,且可編譯運行
  • 提交 PR 前如果確保當前分支在 dev 分支 HEAD 處的話可以不進行 Rebase
  • dev 分支將處於 protected 狀態,非不得已要執行 force push 的話,要提交通知所有開發成員

編碼準則

參考並修改自 Android-Best-Practices 和 Android-Guideline。

Kotlin 源代碼

對類文件使用 駝峰命名法。包名使用 小寫連寫,單詞較多可以使用 _ 分割符。

Property 定義與命名規範

對 Property 的定義應該放在文件的首位,另外請注意 Kotlin 可視修飾符和 Java 的不同,並且遵守以下規範:

  • 要注意 Kotlin 的默認可視修飾符為 public
  • Kotlin 的 internal 修飾符,可以讓目標對象只在同一 Module(IDE 下的 Module) 下可訪問(例如創建一個插件 Module 的時候,可以使用 internal 對外隱藏一些實現細節)
  • 靜態常量命名字母全部大寫,單詞之間用下劃線分隔,且必須使用 const val 修飾符
  • Android SDK中諸如 SharedPreferences,Bundle 和 Intent 等,都採用 key-value 的方式進行賦值,當使用這些組件的時候,key 必須被 private const val 所修飾,並以 KEY_ 作為前綴。
  • Android 下的組件以及控制項盡量以 類型 的縮寫小寫字母作為前綴,例如以下一些可選的前綴(可依此類推):

示例:

internal class TestActivity: Activity() {n compainion object {n const val CONSTANT: Int = 0n private const val KEY_ARG_TITLE = "title"n }n val title: String = "Title"n var listSize: Int? = nulln private var frgHomepage: Fragment? = nulln}n

Kotlin 語言相關

  • 理解好 Kotlin 中的 Function 類型,理解 inline 和 infix 修飾符,掌握 Kotlin 中的 Extensions 和 DSL 的定義,領悟 Function 在 Kotlin 的地位(第一公民)。
  • 看完並理解 stdlib。
  • ByteArray、ShortArray、IntArray 等並不繼承於 Array,它們在 Jvm 中表現為 byte[]... ,所以應該更傾向於選擇它們。
  • 使用 Any 而不是 Object。(注意 Lint 的提示,也會建議使用 Any)
  • 用好 Pair 和 Triple 來避免某些情況新建類。
  • 使用好註解:@Deprecated(標註不推薦的對象)、@ReplaceWith(標註能進行替換的代碼塊)。
  • 注意好 Throwable、Exception 和 Error 的區別,對於可捕捉的錯誤應該使用 Exception 而不是 Throwable。
  • 理解好 apply()、let()、with()、to()、repeat() 的糖用法。
  • 使用 val localA = A!! // or checkNotNull(A) 將 Nullable 變數轉換為 NotNull 類型的 Local Scope 變數。

Log 輸出規範

使用 Log 類列印一些重要的信息對開發者而言是很重要的事情,切記不要使用 Toast 來做信息列印。

VERBOSE 和 DEBUG 類型的 Log 不應該出現在 Release 版本中,INFORMATION、WARNING 和 ERROR 類型的 Log 可以留下來,因為這些信息的輸出能夠幫助我們快速地定位問題所在,當然前提是,需要隱藏重要的信息輸出,如,用戶手機號,郵箱等。

只在 Debug 環境中輸出日誌的小技巧:

if (BuildConfig.DEBUG) Log.d(TAG, "The value of x is " + x)n

類成員排序規範

關於這個並沒有硬性要求,不過好的排序方式,能夠提高可讀性和易學性。這裡給出一些排序建議:

  1. 常量
  2. 欄位
  3. 構造函數
  4. 被重寫的函數(不區分修飾符類型)
  5. 被 private 修飾的函數
  6. 被 public 修飾的函數
  7. 被定義的內部類或者介面

資源文件(Resources)

  • 資源等 .xml 文件應該採用 小寫字母_下劃線 的組合形式,並遵循前綴表明類型的習慣,形如 type_name.xml。
  • res/values 目錄下的文件可以任意命名,但前提是該文件能夠明確表達職責所屬,因為起作用的並不是文件本身,而是內部的標籤屬性。(例如你可以定義 strings_home.xml、colors_home.xml 之類的)

Lyout 相關

  • 布局(Layout)文件命名方式:

布局文件應該與 Android 組件的命名相匹配,以組件類型作為前綴,並且能夠清晰的表達意圖所在。基本規則如下:

值得一提的是,一些布局文件需要通過 Adapter 填充,如 ListView,Recyclerview 等列表視圖,這種場景下,布局的命名應該以 item_ 作為前綴。另外還有一種比較常見的情況,一個布局文件作為另一個布局文件的一部分而存在,或者使用了include,merge 等標籤的布局,可以使用 partial_、include_ 或者 merge_ 作為前綴,這一類布局的命名同樣應該清晰的表達其意圖。

  • Id 命名方式:

控制項 Id 的命名應該以該控制項類型的縮寫作為前綴,和 代碼中的控制項名保持一致。

對於如何排版一個布局文件,請盡量遵循以下規範:

  • 每個屬性獨佔一行,縮進四個空格
  • android:id 作為第一個屬性存在
  • 如果存在 style 屬性,則緊隨 id 之後
  • 如果不存在 style 屬性,則 android:layout_xxx 緊隨 id 之後
  • 當布局中的一個元素不再包含子元素時,另起一行,使用自閉合標籤 />,方便調整和添加新的屬性
  • 善用 IDE 的 Reformat Code 功能,盡量在編輯完 XML 文件後進行格式化

示例如下:

<?xml version="1.0" encoding="utf-8"?>n<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"n xmlns:tools="http://schemas.android.com/tools"n android:layout_width_="match_parent"n android:layout_height="match_parent"n android:orientation="vertical"n >nn <TextViewn android:id="@+id/tvTitle"n stylex="@style/FancyText"n android:layout_width_="match_parent"n android:layout_height="wrap_content"n android:layout_alignParentRight="true"n tools:text="This is title."n />nn <include layout="@layout/partial_header" />nn</LinearLayout>n

  • 避免層級冗餘的嵌套。

Layout 結構優化方面,應盡量避免深層次的布局嵌套,這不僅會引發性能瓶頸,還會帶來項目維護上的麻煩。在書寫布局之前應該對 ViewTree 充分的分析,善用 <merge>標籤 減少層級嵌套,或者使用 Hierarchy Viewer 等 UI 優化工具對 Layout 進行分析與優化。可參考 Optimizing Your UI 與 Optimizing Layout Hierarchies。

Style、Theme 相關

Style 與 Theme 的命名統一使用 駝峰命名法(首字母大寫)。使用多個 Style 文件而不是全部寫在 styles.cml 里,如:style_home.xml,style_item_details.xml,styles_forms.xml 等。

幾乎每個項目都需要適當的使用 Style 文件,因為對於一個視圖來說有一個重複的外觀是很常見的。在應用中對於大多數文本內容,最起碼你應該有一個通用的 Style文 件,例如:

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

應用到 TextView 中:

<TextViewn stylex="@style/ContentText"n android:layout_width_="wrap_content"n android:layout_height="wrap_content"n android:text="@string/price"n />n

你或許需要為按鈕控制項做同樣的事情,不要停止在那裡。將一組相關的和重複的屬性放到一個通用的 Style 中。

對於控制項的 android:layout_xxx 等屬性應該在 Layout 中定義,同時其它屬性 android:xxx 應放在 style 中。核心準則是保證 Layout 屬性(position, margin, size 等)和 content 屬性(text, src 等)在布局文件中,同時將所有的外觀細節屬性(color, padding, font)放在 Style 文件中。

使用 Designtime Attributes(tools 標籤)

  • 布局預覽應使用 tools:xxx 相關屬性,避免 android:text 等硬編碼的出現,具體可參考 Designtime Attributes。示例如下:

<TextViewn android:layout_width_="wrap_content"n android:layout_height="wrap_content"n tools:text="Home Link" n />n

Drawable 相關

  • 常規 Drawable(圖像)文件命名方式:

  • 常規 icon(圖標)文件命名方式:

  • 常規 selector states(選中狀態)文件命名方式:

要注意的是,Selector 的一些狀態是可以疊加的,所以可以產生 btn_order_disabled_focused.9.png 這類命名。

永遠使用 android-selector-chapek 這個插件來生成相應的 Selector Drawable XML 文件,而不應該手工創建。

Color 相關

colors.xml 文件就像個 「調色板」,只映射顏色的 ARGB 值,不應該存在其他類型的數值,不要使用它為不同的按鈕來定義 ARGB 值。應該像下面:

<resources>n <!-- grayscale -->n <color name="white" >#FFFFFF</color>n <color name="gray_light">#DBDBDB</color>n <color name="gray" >#939393</color>n <color name="gray_dark" >#5F5F5F</color>n <color name="black" >#323232</color>nn <!-- basic colors -->n <color name="green" >#27D34D</color>n <color name="blue" >#2A91BD</color>n <color name="orange" >#FF9D2F</color>n <color name="red" >#FF432F</color>n</resources>n

對同一色調,不同色域進行定義時,像 "brand_primary"、"brand_secondary"、 "brand_negative" 這樣的命名也是不錯的選擇。

值得一提的是,這樣規範的顏色很容易修改或重構,App 一共使用了多少種不同的顏色變會得非常清晰。

Dimen 相關

我們應該像對待 colors.xml 一樣對待 dimens.xml 文件,與定義顏色調色板無異,也應該定義一個規範字體大小的 「字型大小板」。

一個很好的建議:

<resources>nn <!-- font sizes -->n <dimen name="font_larger">22sp</dimen>n <dimen name="font_large">18sp</dimen>n <dimen name="font_normal">15sp</dimen>n <dimen name="font_small">12sp</dimen>nn <!-- typical spacing between two views -->n <dimen name="spacing_huge">40dp</dimen>n <dimen name="spacing_large">24dp</dimen>n <dimen name="spacing_normal">14dp</dimen>n <dimen name="spacing_small">10dp</dimen>n <dimen name="spacing_tiny">4dp</dimen>nn <!-- typical sizes of views -->n <dimen name="button_height_tall">60dp</dimen>n <dimen name="button_height_normal">40dp</dimen>n <dimen name="button_height_short">32dp</dimen>nn</resources>n

同樣的,在定義 margin 和 padding 時,可以使用 spacing_xxx 作為前綴對其命名,而不是像對待 String 字元串那樣直接寫值。這樣寫的好處是,使組織結構和修改風格甚至布局變得非常容易。

String 相關

String 命名的前綴應該能夠清楚地表達它的功能職責,如,registration_email_hint,registration_name_hint。如果一個 Sting 不屬於任何模塊,這也就意味著它是通用的,應該遵循以下規範:

推薦閱讀:

Kindle Fire 能夠像一般的 Android 平板一樣使用非 Amazon 內容嗎?
為什麼 HTC 在中國發售的手機相比國際版總是縮水?
Android Studio每日小技巧
中國用戶願意為哪些類型的手機應用付費?

TAG:Android |