這可能是史上最全的Android代碼規(guī)范

前言

無(wú)論你是個(gè)人開(kāi)發(fā)還是團(tuán)隊(duì),一個(gè)良好的代碼規(guī)范,能夠在項(xiàng)目當(dāng)中發(fā)揮舉足輕重的作用;它不僅能使你們的開(kāi)發(fā)更加高效,而且還會(huì)減少BUG產(chǎn)生的幾率,增強(qiáng)代碼可維護(hù)性及穩(wěn)定性。

關(guān)于規(guī)范,我們分兩部分來(lái)講,因?yàn)锳ndroid主要是用Java語(yǔ)言來(lái)寫(xiě)的,所以我們區(qū)別對(duì)待。

JAVA代碼規(guī)范

強(qiáng)制性規(guī)范:

  1. 代碼中的命名均不能以下劃線或美元符號(hào)開(kāi)始,也不能以下劃線或美元符號(hào)結(jié)束。
  2. 代碼中的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。
  3. 類名使用UpperCamelCase 風(fēng)格,必須遵從駝峰形式。
  4. 方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格,必須遵從駝峰形式。
  5. 常量命名全部大寫(xiě),單詞間用下劃線隔開(kāi),力求語(yǔ)義表達(dá)完整清楚,不要嫌名字長(zhǎng)例如:MAX_STOCK_COUNT。
  6. 抽象類命名使用 Abstract 或 Base 開(kāi)頭;異常類命名使用 Exception 結(jié)尾;測(cè)試類 命名以它要測(cè)試的類的名稱開(kāi)始。
  7. 杜絕不規(guī)范的英文縮寫(xiě):AbstractClass 縮寫(xiě)成AbsClass;condition縮寫(xiě)成condi;此類隨意縮寫(xiě)嚴(yán)重降低了代碼的可閱讀性。
  8. 如果使用到了設(shè)計(jì)模式,建議在類名中體現(xiàn)出具體的模式:
   public class ComponentFactory
   public class BufferStrategy
   public class ScrollerProxy
  1. 關(guān)于Service或Dao層的命名

插入:insert(推薦)或save
刪除:delete
修改:update(推薦)或modify
查詢單個(gè)對(duì)象:get
查詢多個(gè)對(duì)象:list

  1. 實(shí)體類必須重載toString()方法,這樣可以通過(guò)調(diào)用對(duì)象的toString()來(lái)排查問(wèn)題。
  2. Object 的 equals 方法容易拋空指針異常,應(yīng)使用常量或確定有值的對(duì)象來(lái)調(diào)用 equals。

正例: "test".equals(object);
反例: object.equals("test");

  1. 避免通過(guò)一個(gè)類的對(duì)象引用訪問(wèn)此類的靜態(tài)變量或靜態(tài)方法,無(wú)謂增加編譯器解析成本,直接用類名來(lái)訪問(wèn)即可。

推薦規(guī)范:

  1. 集合初始化時(shí),盡量指定集合初始值大??;

ArrayList盡量使用ArrayList(int initialCapacity) 初始化 。

  1. 使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進(jìn)行遍歷

說(shuō)明:keySet 其實(shí)是遍歷了 2 次,一次是轉(zhuǎn)為 Iterator 對(duì)象,另一次是從 hashMap 中取出 key 所對(duì)應(yīng)的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。

  1. 高度注意 Map 類集合 K/V 能不能存儲(chǔ) null 值的情況,如下表格:
集合類 Key Value Super 說(shuō)明
Hashtable 不允許為null 不允許為null Dictionary 線程安全
ConcurrentHashMap 不允許為null 不允許為null AbstractMap 分段鎖技術(shù)
TreeMap 不允許為null 允許為null AbstractMap 線程不安全
HashMap 允許為null 允許為null AbstractMap 線程不安全
  1. 利用 Set 元素唯一的特性,可以快速對(duì)一個(gè)集合進(jìn)行去重操作,避免使用 List 的 contains 方法進(jìn)行遍歷、對(duì)比、去重操作。
  2. 通過(guò)雙重檢查鎖(double-checked locking)(在并發(fā)場(chǎng)景)實(shí)現(xiàn)延遲初始化的優(yōu) 化問(wèn)題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問(wèn)題 解決方案中較為簡(jiǎn)單一種(適用于 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ī)范

代碼:

  1. Activity 命名一律使用 模塊名+Activity 的方式。例如,LoginActivity、SignupActivity
  2. Fragment 命名一律使用 模塊名+Fragment 的方式;
  3. 自定義View:Custom(建議)+功能名+View/ViewGroup(具體的組件名稱)。例如:CustomImageScrollerCustomRatingBar。
  4. Widget 小組件:ScanWidget、WeatherWidget
  5. Dialog對(duì)話框:功能名+Dialog。例如:LoginDialog、ProgressDialog
  6. 盡量在每一個(gè)Activity或類中加入TAG,方便我們查看Activity的信息。(Tip : 使用Android Studio提供的快捷鍵logt可快速生成當(dāng)前 類的常量)
  7. 對(duì)于使用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"

然后在具體的頁(yè)面 new Intent(),依次傳遞進(jìn)去值,這樣寫(xiě)其實(shí)沒(méi)什么問(wèn)題;但是試想一下,如果你要調(diào)用的Activity是類似于一個(gè)工具性質(zhì)或通用的Activity(圖片選擇器、登錄、注冊(cè)等等),這時(shí)候你要傳遞的key又很多,如果業(yè)務(wù)復(fù)雜的話,你應(yīng)該會(huì)被這樣冗余且不易閱讀的代碼直接搞崩潰掉。

所以最好的辦法就是在你要調(diào)用Activity提供一個(gè)靜態(tài)工廠方法,要知道靜態(tài)工廠方法所帶來(lái)的好處太多了,由于Activity是不允許通過(guò)new的方式來(lái)初始化的,所以靜態(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);
}

通過(guò)以上方法,我們能夠很好的解耦復(fù)雜的Activity之間的調(diào)用,再加上靜態(tài)方法工廠方法名,代碼可閱讀行大大提高,最終我們看到的調(diào)用NoteActivity將會(huì)是很簡(jiǎn)潔的一段代碼:

NoteActivity.startForCreate(this,noteId);
NoteActivity.startForEdit(this,noteId,content);

此外,Android Studio工具中其實(shí)已經(jīng)在Live Template中提供了這樣的代碼:CMD+J( For MAC OS),簡(jiǎn)單的輸入starter就可以快速地在當(dāng)前的Activity中添加一個(gè)Intent的靜態(tài)操作方法,這其實(shí)也說(shuō)明了Android官方團(tuán)隊(duì)也鼓勵(lì)我們這么做。
如下圖所示:

一下子省了好多代碼,簡(jiǎn)直太贊了有木有!

  1. 增加類注釋,使用Android Studio的 File And Code Template:
  1. 所有的常量加上注釋,且功能相同的排放在一起,不同的進(jìn)行換行;
  2. Activity中變量采用m開(kāi)頭+類名。例如,mTable、mPerson;
  3. Activity中的控件:m+模塊名+控件類型名稱。例如,mLoginEditText,mLoginTextView;

資源Res

1.按照資源的類型,分為以下幾種

控件Id命名:控件縮寫(xiě) _模塊(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)格來(lái)編寫(xiě)代碼。如果你的團(tuán)隊(duì)有自己的一套代碼規(guī)范,然后開(kāi)發(fā)也很高效,代碼也很容易閱讀且可維護(hù),就完全可以按照自己的團(tuán)隊(duì)的技術(shù)規(guī)范來(lái)執(zhí)行。

所以我想表達(dá)的是,在編寫(xiě)的代碼的時(shí)候,能有一個(gè)Guideline(準(zhǔn)則)或者說(shuō)是一個(gè)約定,我們共同遵守這樣的約定,來(lái)達(dá)到我一開(kāi)始說(shuō)的代碼規(guī)范性所帶來(lái)的意義。

正所謂,“離婁之明,公輸子之巧,不以規(guī)矩,不能成方圓。”

參考資料

阿里巴巴Java開(kāi)發(fā)手冊(cè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容