程式設計師必知的Android 程式碼規範
前言
雖然我們專案的程式碼時間並不長,也沒經過太多人手,但程式碼的規範性依然堪憂,目前存在較多的比較自由的「程式碼規範」,這非常不利於專案的維護,程式碼可讀性也不夠高,
此外,客戶端和後端的研發模式也完全不同,後端研發基本都是基於 SOA 思想的,通常一個子系統 3 個人一起維護就已經是很充分的人力了,更多時候就是 1 個主力 + 1 個 backup 的人力配置。
而客戶端卻完全不同,大家的程式碼都是相互交叉的,一個模組的程式碼可能要經歷數十人的蹂躪,所以形成一個一致的開發規範迫在眉睫。
為什麼需要一致的程式碼規範?
核心還是減少溝通成本,提升我們的 Code Review 效率,讓我們的程式碼更加易於維護。此外,一個一致的程式碼規範可以造成更少的 bug,也就意味著更節省時間和金錢。
當然,規範是約定的,本系列文字全是筆者多年來博採眾長,積累而成,所以有任何不同意見,歡迎評論拍磚。
1. Android 的工具規範
工欲善其事,必先利其器。
由於 Android 基本都基於 Android Studio 進行開發,所以工具規範全部以 Android Studio 為前提。
- 必須使用最新的穩定版本的 Android Studio 進行開發;
- 編碼格式必須統一為 UTF-8;
-
刪除多餘的 import,減少警告出現,可利用 AS 的 Optimize Imports(Settings -> Keymap -> Optimize Imports)快捷鍵,設定自己的喜好。
-
編輯完 .java、.kt、.xml 等檔案後必須格式化(需要在設定好以下幾點的前提下)
Reformat Code 的必要性,一定需要保證 IDE 配置一致為前提,儘可能貼切於 Android Studio 預設。 強烈建議對於比較長的老程式碼區域性格式化,不全域性格式化
-
每行字元數不得超過 160 字元,設定 Editor -> Code Style
-
全部設定為單路徑引用,
kotlinx.android.synthetic.main
除外。
以上幾處設定完畢,其他採用 Android Studio 預設方式,再進行 Reformat Code 快捷鍵即可。
2. Android 的分包規範
前面強調了工具的統一配置,再利用 Android Studio 本身的功能便可把程式碼風格變得一致。接下來就帶來第二部分:Android 的分包規範。
對於分包,我們需要達成一致,我們採用 PBF 方式,不推薦使用 PBL 方式。
PBF(按功能分包 Package By Feature) PBL(按層分包 Package By Layer)
PBF 可能不是很好區分在哪個功能中,不過也比 PBL 要好找很多,且 PBF 與 PBL 相比較有如下優勢:
-
package 內高內聚,package 間低耦合 哪塊要添新功能,只改某一個 package 下的東西,而PBL 需要改多個 package,非常麻煩。
-
package 有私有作用域(package-private scope) 原則上一個 package 下的不允許其他類訪問都是不應該加上 public 的。
-
很容易刪除功能 統計發現新功能沒人用,這個版本那塊功能得去掉。如果是 PBL,得從功能入口到整個業務流程把受到牽連的所有能刪的程式碼和 class 都揪出來刪掉,一不小心就完蛋。如果是 PBF,好說,先刪掉對應包,再刪掉功能入口(刪掉包後入口肯定報錯了),完事。
-
高度抽象 解決問題的一般方法是從抽象到具體,PBF 包名是對功能模組的抽象,包內的 class 是實現細節,符合從抽象到具體,而 PBL 弄反了。PBF 從確定 AppName 開始,根據功能模組劃分 package,再考慮每塊的具體實現細節,而 PBL 從一開始就要考慮要不要 dao 層,要不要 com 層等等。
-
只通過 class 來分離邏輯程式碼 PBL 既分離 class 又分離 package,而 PBF 只通過 class 來分離邏輯程式碼。
-
package 的大小有意義了 PBL 中包的大小無限增長是合理的,因為功能越添越多,而 PBF 中包太大(包裡 class 太多)表示這塊需要重構(劃分子包)。
3. Android 的命名規範
程式碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。
注意:即使純拼音命名方式也要避免採用。但國際通用的名稱,可視同英文,比如
toutiao
、douyin
等。
3.1 包名
Android 裡面有 package 的概念,所以需要約定一下包名命名規範。
包名全部小寫,不允許出現中文、大寫字母或者下劃線,前面為子模組命名,再根據 PBF 方式進行命名。
3.2 類名
類名都以 UpperCamelCase
風格編寫。
類名通常是名詞或名詞短語,介面名稱有時可能是形容詞或形容詞短語。現在還沒有特定的規則或行之有效的約定來命名註解型別。
名詞,採用大駝峰命名法,儘量避免縮寫,除非該縮寫是眾所周知的, 比如 HTML、URL,如果類名稱中包含單詞縮寫,則單詞縮寫的每個字母均應大寫。
| 類 | 描述 | 例如 |
| --- | --- | --- |
| Activity
類 | 模組名 + Activity
| 閃屏頁類 SplashActivity
|
| Fragment
類 | 模組名 + Fragment
| 主頁類 HomeFragment
|
| Service
類 | 模組名 + Service
| 時間服務 TimeService
|
| BroadcastReceiver
類 | 功能名 + Receiver
| 推送接收 JPushReceiver
|
| ContentProvider
類 | 功能名 + Provider
| ShareProvider
|
| 自定義 View | 功能名 + View/ViewGroup(元件名稱) | ShapeButton
|
| Dialog對話方塊 | 功能名+Dialog | ImagePickerDialog
|
| Adapter
類 | 模組名 + Adapter
| 課程詳情介面卡 LessonDetailAdapter
|
| 解析類 | 功能名 + Parser
| 首頁解析類 HomePosterParser
|
| 工具方法類 | 功能名 + Utils
或 Manager
| 執行緒池管理類:ThreadPoolManager
日誌工具類:LogUtils
(Logger
也可)
列印工具類:PrinterUtils
|
| 資料庫類 | 功能名 + DBHelper
| 新聞資料庫:NewsDBHelper
|
| 自定義的共享基礎類 | Base
+ 基礎 | BaseActivity
, BaseFragment
|
| 抽象類 | Base
/ Abstract
開頭 | AbstractLogin
|
| 異常類 | Exception
結尾 | LoginException
|
| 介面 | able
/ ible
結尾 / I 開頭 | Runnable
, Accessible
,ILoginView
|
測試類的命名以它要測試的類的名稱開始,以 Test 結束。例如:HashTest
或 HashIntegrationTest
。
介面(interface):命名規則與類一樣採用大駝峰命名法,多以 able 或 ible 結尾,如 interface Runnable
、interface Accessible
。
注意:如果專案採用 MVP,所有 Model、View、Presenter 的介面都以 I 為字首,不加字尾,其他的介面採用上述命名規則。
3.3 方法名
方法名都以 lowerCamelCase
風格編寫。
方法名通常是動詞或動詞短語。
| 方法 | 說明 |
| --- | --- |
| initXX()
| 初始化相關方法,使用 init 為字首標識,如初始化佈局 initView()
|
| isXX()
, checkXX()
| 方法返回值為 boolean 型的請使用 is/check 為字首標識 |
| getXX()
| 返回某個值的方法,使用 get 為字首標識 |
| setXX()
| 設定某個屬性值 |
| handleXX()
, processXX()
| 對資料進行處理的方法 |
| displayXX()
, showXX()
| 彈出提示框和提示資訊,使用 display/show 為字首標識 |
| updateXX()
| 更新資料 |
| saveXX()
, insertXX()
| 儲存或插入資料 |
| resetXX()
| 重置資料 |
| clearXX()
| 清除資料 |
| removeXX()
, deleteXX()
| 移除資料或者檢視等,如 removeView()
|
| drawXX()
| 繪製資料或效果相關的,使用 draw 字首標識 |
3.4 變數命名
這裡的變數為廣義的變數,包括了常量、區域性變數、全域性變數等,它們的基礎規則是:
- 型別需要是名詞 / 名詞短語;
- 採用
lowerCamelCase
風格;
在具體的變數命名時,會根據該變數的型別不同而附加額外的命名規則:
| 型別 | 說明 | 例如 |
| --- | --- | --- |
| 常量 | 大寫 & 下劃線隔開,Kotlin 一定要 const val | const val TYPE_NORMAL = 1
static final TYPE_NORMAL = 1
|
| 臨時變數名 | 整型:i
、j
、k
、m
、n
,字元型一般用 c
、d
、e
| for(int i = 0;i < len; i++)
|
| 其他變數 | lowerCamelCase
風格即可,私有變數也不要使用 m
開頭 | private int tmp;
|
| Kotlin | 只讀變數使用 val
,可變變數使用 var
,儘可能使用 val
| var tmp = 0
val defaultIndex = 0
|
3.5 資源命名
Android 的資源包括:
資原始檔命名為全部小寫,採用下劃線命名法。
3.5.1 動畫資原始檔(anim/ 和 animator/)
安卓主要包含屬性動畫和檢視動畫,其檢視動畫包括補間動畫和逐幀動畫。屬性動畫檔案需要放在 res/animator/
目錄下,檢視動畫檔案需放在 res/anim/
目錄下。命名規則:{模組名_}邏輯名稱
。
說明:
{}
中的內容為可選,邏輯名稱
可由多個單詞加下劃線組成。例如:refresh_progress.xml
、market_cart_add.xml
、market_cart_remove.xml
。
如果是普通的補間動畫或者屬性動畫,可採用:動畫型別_方向
的命名方式。
例如:
| 名稱 | 說明 |
| --- | --- |
| fade_in
| 淡入 |
| fade_out
| 淡出 |
| push_down_in
| 從下方推入 |
| push_down_out
| 從下方推出 |
| push_left
| 推向左方 |
| slide_in_from_top
| 從頭部滑動進入 |
| zoom_enter
| 變形進入 |
| slide_in
| 滑動進入 |
| shrink_to_middle
| 中間縮小 |
3.5.2 顏色資原始檔(color/)
color/ 是專門用於存放顏色相關資源的資料夾。命名規則:型別{_模組名}_邏輯名稱
。
說明:
{}
中的內容為可選。例如:sel_btn_font.xml
。
顏色資源也可以放於 res/drawable/
目錄,引用時則用 @drawable
來引用,但不推薦這麼做,最好還是把兩者分開。
3.5.3 圖片資原始檔(drawable/ 和 mipmap/)
res/drawable/
目錄下放的是點陣圖檔案(.png、.9.png、.jpg、.gif)或編譯為可繪製物件資源子型別的 XML 檔案,而 res/mipmap/
目錄下放的是不同密度的啟動圖示,所以 res/mipmap/
只用於存放啟動圖示,其餘圖片資原始檔都應該放到 res/drawable/
目錄下。
命名規則:型別{_模組名}_邏輯名稱
、型別{_模組名}_顏色
。
說明:
{}
中的內容為可選;型別
可以是可繪製物件資源型別,也可以是控制元件型別最後可加字尾_small
表示小圖,_big
表示大圖。
例如:
| 名稱 | 說明 |
| --- | --- |
| btn_main_about.png
| 主頁關於按鍵 型別_模組名_邏輯名稱
|
| btn_back.png
| 返回按鍵 型別_邏輯名稱
|
| divider_maket_white.png
| 商城白色分割線 型別_模組名_顏色
|
| ic_edit.png
| 編輯圖示 型別_邏輯名稱
|
| bg_main.png
| 主頁背景 型別_邏輯名稱
|
| btn_red.png
| 紅色按鍵 型別_顏色
|
| btn_red_big.png
| 紅色大按鍵 型別_顏色
|
| ic_avatar_small.png
| 小頭像圖示 型別_邏輯名稱
|
| bg_input.png
| 輸入框背景 型別_邏輯名稱
|
| divider_white.png
| 白色分割線 型別_顏色
|
| bg_main_head.png
| 主頁頭部背景 型別_模組名_邏輯名稱
|
| def_search_cell.png
| 搜尋頁面預設單元圖片 型別_模組名_邏輯名稱
|
| ic_more_help.png
| 更多幫助圖示 型別_邏輯名稱
|
| divider_list_line.png
| 列表分割線 型別_邏輯名稱
|
| sel_search_ok.xml
| 搜尋介面確認選擇器 型別_模組名_邏輯名稱
|
| shape_music_ring.xml
| 音樂介面環形形狀 型別_模組名_邏輯名稱
|
如果有多種形態,如按鈕選擇器:sel_btn_xx.xml
,採用如下命名:
| 名稱 | 說明 |
| --- | --- |
| sel_btn_xx
| 作用在 btn_xx
上的 selector
|
| btn_xx_normal
| 預設狀態效果 |
| btn_xx_pressed
| state_pressed
點選效果 |
| btn_xx_focused
| state_focused
聚焦效果 |
| btn_xx_disabled
| state_enabled
不可用效果 |
| btn_xx_checked
| state_checked
選中效果 |
| btn_xx_selected
| state_selected
選中效果 |
| btn_xx_hovered
| state_hovered
懸停效果 |
| btn_xx_checkable
| state_checkable
可選效果 |
| btn_xx_activated
| state_activated
啟用效果 |
| btn_xx_window_focused
| state_window_focused
視窗聚焦效果 |
注意:使用 Android Studio 的外掛 SelectorChapek 可以快速生成 selector,前提是命名要規範。
3.5.4 佈局資原始檔(layout/)
命名規則:型別_模組名
、{模組名_}型別_邏輯名稱
。(也採用 PBF,方便檢視,尤其在大專案中)
說明:
{}
中的內容為可選。
例如:
| 型別 | 名稱 | 說明 |
| --- | --- | --- |
| Activity
| main_activity.xml
| 主窗體 模組名_型別
|
| Fragment
| music_fragment.xml
| 音樂片段 模組名_型別
|
| Dialog
| loading_dialog.xml
| 載入對話方塊 邏輯名稱_型別
|
| PopupWindow
| info_ppw.xml
| 資訊彈窗(PopupWindow) 邏輯名稱_型別
|
| adapter
的列表項 | main_song_item.xml
| 主頁歌曲列表項 模組名_型別_邏輯名稱
|
3.5.5 佈局資源 id 命名
命名規則:{模組名_}_邏輯名_view 縮寫(功能)
,例如: main_search_btn
、back_btn
。此外,採用 Kotlinx 直接獲取佈局檔案的時候,id 命名採用駝峰樣式。
說明:
{}
中的內容為可選。參考 GoogleSamples Demo:http://github.com/android/architecture-samples
例如:
| 型別 | 規範 | 命名示例 |
| --- | --- | --- |
| TextView
| xxx_text
| user_login_text
|
| EditText
| xxx_edit
| user_login_edit
|
| ImageView
| xxx_iv
| user_login_iv
|
| Button
| xxx_btn
| user_login_btn
|
| CheckBox
| xxx_cb
| user_login_cb
|
| GridView
| xxx_gv
| user_login_gv
|
| ListView
| xxx_lv
| user_login_lv
|
| RecyclerView
| xxx_rv
| user_login_rv
|
| RadioButton
| xxx_rb
| user_login_rb
|
| LinearLayout
| xxx_ll
| user_login_ll
|
| RelativeLayout
| xxx_rl
| user_login_rl
|
| FrameLayout
| xxx_fl
| user_login_fl
|
| GridLayout
| xxx_gl
| user_login_gl
|
| ConstraintLayout
| xxx_cl
| user_login_cl
|
3.5.6 選單資原始檔(menu/)
選單相關的資原始檔應放在該目錄下。命名規則:{模組名_}邏輯名稱
說明:
{}
中的內容為可選。例如:main_drawer.xml
、navigation.xml
。
3.5.7 colors.xml
<color>
的 name
命名使用下劃線命名法,在你的 colors.xml
檔案中應該只是對映顏色的名稱一個 ARGB 值,而沒有其它的。不要使用它為不同的按鈕來定義 ARGB 值。
例如,不要像下面這樣做:
<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>
使用這種格式,會非常容易重複定義 ARGB 值,而且如果應用要改變基色的話會非常困難。同時,這些定義是跟一些環境關聯起來的,如 button
或者 comment
,應該放到一個按鈕風格中,而不是在 colors.xml
檔案中。
相反,應該這樣做:
```
<!-- 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>
```
嚮應用設計者那裡要這個調色盤,名稱不需要跟 "green"
、"blue"
等等相同。"brand_primary"
、"brand_secondary"
、"brand_negative"
這樣的名字也是完全可以接受的。像這樣規範的顏色很容易修改或重構,會使應用一共使用了多少種不同的顏色變得非常清晰。通常一個具有審美價值的 UI 來說,減少使用顏色的種類是非常重要的。
注意:如果某些顏色和主題有關,那就單獨寫一個
colors_theme.xml
。
3.5.8 strings.xml
<string>
的 name
命名使用下劃線命名法,採用以下規則:{模組名_}邏輯名稱
,這樣方便同一個介面的所有 string
都放到一起,方便查詢。
| 名稱 | 說明 |
| --- | --- |
| main_menu_about
| 主選單按鍵文字 |
| friend_title
| 好友模組標題欄 |
| friend_dialog_del
| 好友刪除提示 |
| login_check_email
| 登入驗證 |
| dialog_title
| 彈出框標題 |
| button_ok
| 確認鍵 |
| loading
| 載入文字 |
3.5.9 styles.xml
<style>
的 name
命名使用大駝峰命名法,幾乎每個專案都需要適當的使用 styles.xml
檔案,因為對於一個檢視來說,有一個重複的外觀是很常見的,將所有的外觀細節屬性(colors
、padding
、font
)放在 styles.xml
檔案中。 在應用中對於大多數文字內容,最起碼你應該有一個通用的 styles.xml
檔案,例如:
```
```
應用到 TextView
中:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"/>
或許你需要為按鈕控制元件做同樣的事情,不要停止在那裡,將一組相關的和重複 android:xxxx
的屬性放到一個通用的 <style>
中。
4. Android 的註釋規範
4.1 類註釋
每個類完成後應該有作者姓名和聯絡方式的註釋,對自己的程式碼負責。
/**
* <pre>
* author : nanchen
* e-mail : xxx@xx
* time : 2021/01/18
* desc : xxxx 描述
* version: 1.0
* </pre>
*/
public class WelcomeActivity {
...
}
具體可以在 AS 中自己配製,進入 Settings -> Editor -> File and Code Templates -> Includes -> File Header,輸入
/**
* <pre>
* author : ${USER}
* e-mail : xxx@xx
* time : ${YEAR}/${MONTH}/${DAY}
* desc :
* version: 1.0
* </pre>
*/
這樣便可在每次新建類的時候自動加上該頭註釋。
4.2 方法註釋
每一個成員方法(包括自定義成員方法、覆蓋方法、屬性方法)的方法頭都必須做方法頭註釋,在方法前一行輸入 /** + 回車
或者設定 Fix doc comment
(Settings -> Keymap -> Fix doc comment)快捷鍵,AS 便會幫你生成模板,我們只需要補全引數即可,如下所示。@param
, @return
, @throws
, @deprecated
這 4 種標記出現的時候,描述都不能為空。當描述無法在一行中容納,連續行至少需要再縮排 4 個空格。
/**
* Report an accessibility action to this view's parents for delegated processing.
*
* <p>Implementations of {@link #performAccessibilityAction(int, Bundle)} may internally
* call this method to delegate an accessibility action to a supporting parent. If the parent
* returns true from its
* {@link ViewParent#onNestedPrePerformAccessibilityAction(View, int, android.os.Bundle)}
* method this method will return true to signify that the action was consumed.</p>
*
* <p>This method is useful for implementing nested scrolling child views. If
* {@link #isNestedScrollingEnabled()} returns true and the action is a scrolling action
* a custom view implementation may invoke this method to allow a parent to consume the
* scroll first. If this method returns true the custom view should skip its own scrolling
* behavior.</p>
*
* @param action Accessibility action to delegate
* @param arguments Optional action arguments
* @return true if the action was consumed by a parent
*/
public boolean dispatchNestedPrePerformAccessibilityAction(int action, Bundle arguments) {
for (ViewParent p = getParent(); p != null; p = p.getParent()) {
if (p.onNestedPrePerformAccessibilityAction(this, action, arguments)) {
return true;
}
}
return false;
}
4.3 塊註釋
塊註釋與其周圍的程式碼在同一縮排級別。它們可以是 /* ... */
風格,也可以是 // ...
風格(//
後最好帶一個空格)。對於多行的 /* ... */
註釋,後續行必須從 *
開始, 並且與前一行的 *
對齊。以下示例註釋都是 OK 的。
``` / * This is okay. /
// And so // is this.
/ Or you can * even do this. / ```
註釋不要封閉在由星號或其它字元繪製的框架裡。
Tip:在寫多行註釋時,如果你希望在必要時能重新換行(即註釋像段落風格一樣),那麼使用
/* ... */
。
比如:
4.4 全域性變數的註釋
全域性變數的註釋樣式如下(注意註釋之間有空格):
``` /* * The next available accessibility id. / private static int nextAccessibilityViewId;
/* * The animation currently associated with this view. / protected Animation currentAnimation = null; ```
4.5 其他一些註釋
AS 已幫你集成了一些註釋模板,我們只需要直接使用即可,在程式碼中輸入 TODO
、FIXME
等這些註釋模板,回車後便會出現如下注釋。
// TODO: 17/3/14 需要實現,但目前還未實現的功能的說明
// FIXME: 17/3/14 需要修正,甚至程式碼是錯誤的,不能工作,需要修復的說明
4.5 註釋必須遵守的規範
4.5.1 不言自明的方法不要加註釋。
比如 Item getItem(int index)
是一段自說明的程式碼,我們可以直接從方法的命名就能知道它是幹嘛的,所以不需要增加註釋。
4.5.2 提測的程式碼不應該有 TODO 這樣的註釋
5. 程式碼樣式規範
5.1 使用標準大括號樣式
左大括號不單獨佔一行,與其前面的程式碼位於同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要在條件語句周圍新增大括號。例外情況:如果整個條件語句(條件和主體)適合放在同一行,那麼您可以(但不是必須)將其全部放在一行上。例如,我們接受以下樣式:
if (condition) {
body();
}
同樣也接受以下樣式:
if (condition) body();
但不接受以下樣式:
if (condition)
body(); // bad!
5.2 編寫簡短方法
在可行的情況下,儘量編寫短小精煉的方法。我們瞭解,有些情況下較長的方法是恰當的,因此對方法的程式碼長度沒有做出硬性限制。如果某個方法的程式碼超出 40 行,請考慮是否可以在不破壞程式結構的前提下對其拆解。
5.3 類成員的順序
這並沒有唯一的正確解決方案,但如果都使用一致的順序將會提高程式碼的可讀性,推薦使用如下排序:
- 常量(Kotlin 伴生物件放在開頭)
- 欄位
- 建構函式
- 重寫函式和回撥
- 公有函式
- 私有函式
- 內部類或介面
例如:
``` public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private String mTitle;
private TextView mTextViewTitle;
@Override
public void onCreate() {
...
}
public void setTitle(String title) {
mTitle = title;
}
private void setUpView() {
...
}
static class AnInnerClass {
}
} ```
如果類繼承於 Android 元件(例如 Activity
或 Fragment
),那麼把重寫函式按照他們的生命週期進行排序是一個非常好的習慣,例如,Activity
實現了 onCreate()
、onDestroy()
、onPause()
、onResume()
,它的正確排序如下所示:
``` public class MainActivity extends Activity { //Order matches Activity lifecycle @Override public void onCreate() {}
@Override
public void onResume() {}
@Override
public void onPause() {}
@Override
public void onDestroy() {}
} ```
5.4 函式引數的排序
在 Android 開發過程中,Context
在函式引數中是再常見不過的了,我們最好把 Context
作為其第一個引數。
正相反,我們把回撥介面應該作為其最後一個引數。
例如:
``` // Context always goes first public User loadUser(Context context, int userId);
// Callbacks always go last public void loadUserAsync(Context context, int userId, UserCallback callback); ```
5.5 字串常量的命名和值
Android SDK 中的很多類都用到了鍵值對函式,比如 SharedPreferences
、Bundle
、Intent
,所以,即便是一個小應用,我們最終也不得不編寫大量的字串常量。
當時用到這些類的時候,我們 必須 將它們的鍵定義為 static final
欄位,並遵循以下指示作為字首。
| 類 | 欄位名字首 |
| --- | --- |
| SharedPreferences | PREF_
|
| Bundle | BUNDLE_
|
| Fragment Arguments | ARGUMENT_
|
| Intent Extra | EXTRA_
|
| Intent Action | ACTION_
|
說明:雖然 Fragment.getArguments()
得到的也是 Bundle
,但因為這是 Bundle
的常用用法,所以特意為此定義一個不同的字首。
例如:
``` // 注意:欄位的值與名稱相同以避免重複問題 static final String PREF_EMAIL = "PREF_EMAIL"; static final String BUNDLE_AGE = "BUNDLE_AGE"; static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// 與意圖相關的項使用完整的包名作為值的字首 static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME"; static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER"; ```
5.6 行長限制
程式碼中每一行文字的長度都應該不超過 160 個字元。雖然關於此規則存在很多爭論,但最終決定仍是以 160 個字元為上限,如果行長超過了 160(AS 視窗右側的豎線就是設定的行寬末尾 ),我們通常有兩種方法來縮減行長。
- 提取一個區域性變數或方法(最好)。
- 使用換行符將一行換成多行。
不過存在以下例外情況:
- 如果備註行包含長度超過 160 個字元的示例命令或文字網址,那麼為了便於剪下和貼上,該行可以超過 160 個字元。
- 匯入語句行可以超出此限制,因為使用者很少會看到它們(這也簡化了工具編寫流程)。
5.6.1 換行策略
這沒有一個準確的解決方案來決定如何換行,通常不同的解決方案都是有效的,但是有一些規則可以應用於常見的情況。
5.6.1.1 操作符的換行
除賦值操作符之外,我們把換行符放在操作符之前,例如:
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
賦值操作符的換行我們放在其後,例如:
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
5.6.1.2 函式鏈的換行
當同一行中呼叫多個函式時(比如使用構建器時),對每個函式的呼叫應該在新的一行中,我們把換行符插入在 .
之前。
例如:
Picasso.with(context).load("http://blankj.com/images/avatar.jpg").into(ivAvatar);
我們應該使用如下規則:
Picasso.with(context)
.load("http://blankj.com/images/avatar.jpg")
.into(ivAvatar);
5.6.1.3 多引數的換行
當一個方法有很多引數或者引數很長的時候,我們應該在每個 ,
後面進行換行。
比如:
loadPicture(context, "http://blankj.com/images/avatar.jpg", ivAvatar, "Avatar of the user", clickListener);
我們應該使用如下規則:
loadPicture(context,
"http://blankj.com/images/avatar.jpg",
ivAvatar,
"Avatar of the user",
clickListener);
5.6.1.4 RxJava 鏈式的換行
RxJava 的每個操作符都需要換新行,並且把換行符插入在 .
之前。
例如:
public Observable<Location> syncLocations() {
return mDatabaseHelper.getAllLocations()
.concatMap(new Func1<Location, Observable<? extends Location>>() {
@Override
public Observable<? extends Location> call(Location location) {
return mRetrofitService.getLocation(location.id);
}
})
.retry(new Func2<Integer, Throwable, Boolean>() {
@Override
public Boolean call(Integer numRetries, Throwable throwable) {
return throwable instanceof RetrofitError;
}
});
}
我的庫存,需要的小夥伴請點選我的GitHub免費領取
文末
您的點贊收藏就是對我最大的鼓勵! 歡迎關注我,分享Android乾貨,交流Android技術。 對文章有何見解,或者有何技術問題,歡迎在評論區一起留言討論!