
前言
無論你是個(gè)人開發(fā)還是團(tuán)隊(duì),一個(gè)良好的代碼規(guī)范,能夠在項(xiàng)目當(dāng)中發(fā)揮舉足輕重的作用;它不僅能使你們的開發(fā)更加高效,而且還會減少BUG產(chǎn)生的幾率,增強(qiáng)代碼可維護(hù)性及穩(wěn)定性。
關(guān)于規(guī)范,我們分兩部分來講,因?yàn)锳ndroid主要是用Java語言來寫的,所以我們區(qū)別對待。
JAVA代碼規(guī)范
強(qiáng)制性規(guī)范:
- 代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結(jié)束。
- 代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。
- 類名使用UpperCamelCase 風(fēng)格,必須遵從駝峰形式。
- 方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格,必須遵從駝峰形式。
- 常量命名全部大寫,單詞間用下劃線隔開,力求語義表達(dá)完整清楚,不要嫌名字長例如:MAX_STOCK_COUNT。
- 抽象類命名使用 Abstract 或 Base 開頭;異常類命名使用 Exception 結(jié)尾;測試類 命名以它要測試的類的名稱開始。
- 杜絕不規(guī)范的英文縮寫:AbstractClass 縮寫成AbsClass;condition縮寫成condi;此類隨意縮寫嚴(yán)重降低了代碼的可閱讀性。
- 如果使用到了設(shè)計(jì)模式,建議在類名中體現(xiàn)出具體的模式:
public class ComponentFactory
public class BufferStrategy
public class ScrollerProxy
- 關(guān)于Service或Dao層的命名
插入:insert(推薦)或save
刪除:delete
修改:update(推薦)或modify
查詢單個(gè)對象:get
查詢多個(gè)對象:list
- 實(shí)體類必須重載toString()方法,這樣可以通過調(diào)用對象的toString()來排查問題。
- Object 的 equals 方法容易拋空指針異常,應(yīng)使用常量或確定有值的對象來調(diào)用 equals。
正例: "test".equals(object);
反例: object.equals("test");
- 避免通過一個(gè)類的對象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,直接用類名來訪問即可。
推薦規(guī)范:
- 集合初始化時(shí),盡量指定集合初始值大??;
ArrayList盡量使用ArrayList(int initialCapacity) 初始化 。
- 使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進(jìn)行遍歷
說明:keySet 其實(shí)是遍歷了 2 次,一次是轉(zhuǎn)為 Iterator 對象,另一次是從 hashMap 中取出 key 所對應(yīng)的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
- 高度注意 Map 類集合 K/V 能不能存儲 null 值的情況,如下表格:
| 集合類 | Key | Value | Super | 說明 |
|---|---|---|---|---|
| Hashtable | 不允許為null | 不允許為null | Dictionary | 線程安全 |
| ConcurrentHashMap | 不允許為null | 不允許為null | AbstractMap | 分段鎖技術(shù) |
| TreeMap | 不允許為null | 允許為null | AbstractMap | 線程不安全 |
| HashMap | 允許為null | 允許為null | AbstractMap | 線程不安全 |
- 利用 Set 元素唯一的特性,可以快速對一個(gè)集合進(jìn)行去重操作,避免使用 List 的 contains 方法進(jìn)行遍歷、對比、去重操作。
- 通過雙重檢查鎖(double-checked locking)(在并發(fā)場景)實(shí)現(xiàn)延遲初始化的優(yōu) 化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題 解決方案中較為簡單一種(適用于 JDK5 及以上版本),將目標(biāo)屬性聲明為 volatile 型。
*反例*:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other functions and members...
}
Android代碼規(guī)范
代碼:
- Activity 命名一律使用 模塊名+Activity 的方式。例如,LoginActivity、SignupActivity;
- Fragment 命名一律使用 模塊名+Fragment 的方式;
- 自定義View:Custom(建議)+功能名+View/ViewGroup(具體的組件名稱)。例如:CustomImageScroller、CustomRatingBar。
- Widget 小組件:ScanWidget、WeatherWidget。
- Dialog對話框:功能名+Dialog。例如:LoginDialog、ProgressDialog
- 盡量在每一個(gè)Activity或類中加入TAG,方便我們查看Activity的信息。(Tip : 使用Android Studio提供的快捷鍵logt可快速生成當(dāng)前 類的常量)
- 對于使用Intent傳遞數(shù)據(jù),聲明一些Key的時(shí)候:
EXTRA_KEY_+具體Key名稱,例如我們現(xiàn)在有一個(gè)人的名字和年齡要傳那么首先定義:
public static final String EXTRA_KEY_PERSON_NAME="EXTRA_KEY_PERSON_NAME"
public static final String EXTRA_KEY_PERSON_AGE="EXTRA_KEY_PERSON_AGE"
然后在具體的頁面 new Intent(),依次傳遞進(jìn)去值,這樣寫其實(shí)沒什么問題;但是試想一下,如果你要調(diào)用的Activity是類似于一個(gè)工具性質(zhì)或通用的Activity(圖片選擇器、登錄、注冊等等),這時(shí)候你要傳遞的key又很多,如果業(yè)務(wù)復(fù)雜的話,你應(yīng)該會被這樣冗余且不易閱讀的代碼直接搞崩潰掉。
所以最好的辦法就是在你要調(diào)用Activity提供一個(gè)靜態(tài)工廠方法,要知道靜態(tài)工廠方法所帶來的好處太多了,由于Activity是不允許通過new的方式來初始化的,所以靜態(tài)工廠方法的好處在此就不那么明顯,但是已經(jīng)足夠我們優(yōu)化我們的代碼了。舉個(gè)例子,我們有一個(gè)筆記 NoteActivity,用于創(chuàng)建筆記和修改筆記,
//筆記Id
private static final String EXTRA_KEY_NOTE_ID ="EXTRA_KEY_NOTE_ID" ;
//筆記內(nèi)容
private static final String EXTRA_KEY_NOTE_CONTENT ="EXTRA_KEY_NOTE_CONTENT" ;
//筆記模式
private static final String EXTRA_KEY_NOTE_MODE ="EXTRA_KEY_NOTE_MODE" ;
//用于創(chuàng)建筆記
public static void startForCreate(Context context, int noteId) {
start(context, noteId, null, MODE_CREATE);
}
//用于編輯筆記
public static void startForEdit(Context context, int noteId, String content) {
start(context, noteId, content, MODE_UPDATE);
}
public static void start(Context context, int noteId, String content, int mode) {
Intent starter = new Intent(context, TableShareListSettingActivity.class);
starter.putExtra(EXTRA_KEY_NOTE_ID,noteId);
starter.putExtra(EXTRA_KEY_NOTE_CONTENT,content);
starter.putExtra(EXTRA_KEY_NOTE_CONTENT,mode);
context.startActivity(starter);
}
通過以上方法,我們能夠很好的解耦復(fù)雜的Activity之間的調(diào)用,再加上靜態(tài)方法工廠方法名,代碼可閱讀行大大提高,最終我們看到的調(diào)用NoteActivity將會是很簡潔的一段代碼:
NoteActivity.startForCreate(this,noteId);
NoteActivity.startForEdit(this,noteId,content);
此外,Android Studio工具中其實(shí)已經(jīng)在Live Template中提供了這樣的代碼:CMD+J( For MAC OS),簡單的輸入starter就可以快速地在當(dāng)前的Activity中添加一個(gè)Intent的靜態(tài)操作方法,這其實(shí)也說明了Android官方團(tuán)隊(duì)也鼓勵我們這么做。
如下圖所示:

一下子省了好多代碼,簡直太贊了有木有!
- 增加類注釋,使用Android Studio的 File And Code Template:


- 所有的常量加上注釋,且功能相同的排放在一起,不同的進(jìn)行換行;
- Activity中變量采用m開頭+類名。例如,mTable、mPerson;
- Activity中的控件:m+模塊名+控件類型名稱。例如,mLoginEditText,mLoginTextView;
資源Res
1.按照資源的類型,分為以下幾種
控件Id命名:控件縮寫 _模塊(module) _功能名(function)
| 控件類型 | ID命名規(guī)則 |
|---|---|
| TextView | tv_module_function |
| EditText | et_module_function |
| ImageView | iv_module_function |
| Button | btn_module_function |
| ListView | lv_module_function |
| GridView | gv_module_function |
| CheckBox | check_module_function |
| RadioButton | radio_module_function |
| LinearLayout | ll_module_function |
| RelativeLayout | rl_module_function |
| FrameLayout | fl_module_function |
| GridLayout | gl_module_function |
| ··· | ··· |
Color資源命名
| Resources Type | 命名規(guī)則 |
|---|---|
| color | 組件名+具體作用名。例 R.color.button_text |
String資源命名
| Resources Type | 命名規(guī)則 |
|---|---|
| string | 具體功能。 例 R.string.hello |
Drawable資源命名
| Resources Type | 命名規(guī)則 |
|---|---|
| launcher icon | ic_launcher。例R.drawable.ic_launcher |
| normal icon | ic_具體模塊_功能。例R.drawable.ic_audio_pause |
| Toolbar icon | ic_ab_功能名。例如ic_ab_search |
| selector | selector_模塊_功能名。例如 selector_login_button |
| shape | shape_模塊功能名狀態(tài)。例如 R.drawable.shape_login_button_normal |
Layout資源命名
| 類型 | 命名規(guī)則 |
|---|---|
| activity | activity_模塊名。例如 R.layout.activity_login |
| fragment | fragment_模塊名。例如 R.layout.fragment_login_layout_header |
| include | layout_模塊名_功能名。例如 @layout/layout_login_bottom |
| adapter | adapter_item_模塊名_功能名。例如 R.layout.adapter_item_simple_text |
| dialog | dialog_模塊_功能名。例如 R.layout.dialog_time_picker |
| list header | header_模塊_功能。例如 R.layout.header_main_top_ad |
| list footer | footer_模塊_功能。例如 R.layout.footer_main_bottom_action |
| widget | widget_模塊_功能。例如 R.layout.widget_app_clock |
| ··· | ··· |
Menu資源命名
| Resources Type | 命名規(guī)則 |
|---|---|
| menu | menu_模塊名。例如 menu_login |
Values資源命名
| Resources Type | 命名規(guī)則 |
|---|---|
| color | 模塊名_color。例如 material_design_color |
| dimens | 模塊名_dimens。例如 material_design_dimens |
| style | 模塊名_style。例如 material_design_style |
| themes | 模塊名_themes。例如 material_design_themes |
總結(jié)
其實(shí)代碼規(guī)范只是一個(gè)Guideline,不是一定要采取某種特定的風(fēng)格來編寫代碼。如果你的團(tuán)隊(duì)有自己的一套代碼規(guī)范,然后開發(fā)也很高效,代碼也很容易閱讀且可維護(hù),就完全可以按照自己的團(tuán)隊(duì)的技術(shù)規(guī)范來執(zhí)行。
所以我想表達(dá)的是,在編寫的代碼的時(shí)候,能有一個(gè)Guideline(準(zhǔn)則)或者說是一個(gè)約定,我們共同遵守這樣的約定,來達(dá)到我一開始說的代碼規(guī)范性所帶來的意義。
正所謂,“離婁之明,公輸子之巧,不以規(guī)矩,不能成方圓?!?/p>