Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發規范

Android開發規范

編輯:關於Android編程

1 介紹

1.1 目的

減少維護花費。 提高可讀性。 加快工作交接。 減少名字增生。 降低缺陷引入的機會。

1.2 術語和定義

強制:編程時必須遵守的規定,含有強制字樣或字體用加粗式樣標注。

推薦:編程時推薦遵守的規定,字體用普通式樣標注。

2 文件組織

避免超過 2000 行的源文件。

2.1 Java 包和源文件

每個 Java 源文件都包含一個單一的公共類或接口。若私有類和接口與一個公共類相關聯,可以將它們和公共類放入同一個源文件。公共類必須是這個文件中的第一個類或接口

Java 源文件還遵循以下規則:

  • 開頭注釋
  • 包和引入語句
  • 類和接口聲明

2.1.1 開頭注釋

所有的源文件都應該在開頭有一個的注釋,其中列出版本信息、日期和版權聲明。

/*
 * Copyright (c) 2014 SIMCOM, Inc.
 * All Rights Reserved.
 * SIMCOM Proprietary and Confidential.
 */

2.1.2 包和引入語句

在多數 Java 源文件中,第一個非注釋行是包語句,在它之後可以跟引入語句。

package com.android.sim;

import java.io.IOException;

在導入包時當完全限制代碼所使用的類的名字,盡量少用通配符的方式,但導入一些通用包,或用到一個包下大部分類時,則可是使用通配符方式。同一包中的類在導入時應聲明在一起,無效的未使用到的引用要即時刪除。

2.1.3 類和接口聲明

下表(強制)描述了類和接口聲明的各個部分以及它們出現的先後次序。

類/接口聲明的各部分 說明 類/接口文檔注釋(/**……*/) 類和接口應該有標准 Javadoc 注釋。 類或接口的聲明 類名和接口的第一個字符大寫,每個單詞的首字母大寫,中間可以有下劃線。 類/接口實現的長度限制 限制一個匿名內部類的長度不要超過 80 行。 類的(靜態)變量 首先是類的公共變量,隨後是保護變量,再後是包一級別的變量(沒有訪問修飾符,access modifier),最後是私有變量。靜態 final 變量應全部大寫,中間可以有下劃線。 實例變量   構造器 構造器的代碼長度(不計空行),不應超過 200 行。 方法 方法名應為動詞或動賓短語,首字母小寫,其後每個單詞首字母大寫,方法的參數不要超過 8個,參數名字必須和變量的命名規范一致,public 的方法之前應該有 Javadoc 注釋,方法之後的大括號位於行尾。方法應該保持簡短和重點突出,對方法的代碼長度並沒有硬性的限制。如果方法代碼超過了 40 行,就該考慮是否可以在不損害程序結構的前提下進行分拆。

3 縮進排版

4 個空格作為縮進排版的一個單位,不使用制表符 tab。

8 個空格作為換行後的縮進,包括函數調用和賦值。

Instrument i =
        someLongexpression_r(that, NotFit, on, one, line);    // 推薦

Instrument i =
    someLongexpression_r(that, NotFit, on, one, line);        // 避免

3.1 行長度

盡量避免一行的長度超過 100 個字符。

例外:如果注釋行包含了超過 100 個字符的命令示例或者 url 文字,為了便於剪切和復制,其長度可以超過 100 個字符。

例外:import 行可以超過限制,因為很少有人會去閱讀它。這也簡化了編程工具的寫入操作。

3.2 括號

大括號不單獨占用一行,應緊接著上一行書寫。

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

我們需要用大括號來包裹條件語句塊。不過也有例外,如果整個條件語句塊(條件和語句本身)都能容納在一行內,也可以(但不是必須)把它們放入同一行中。也就是說,這是合法的:

if (condition) {
    body();
}                    	  // 推薦
if (condition) body();    // 避免
if (condition)
    body();          	  // 錯誤

3.3 換行

當一個表達式無法容納在一行內時,可以依據如下一般規則斷開:

  • 在一個逗號後面斷開。
  • 在一個操作符前面斷開。
  • 寧可選擇較高級別的斷開,而非較低級別的斷開。
  • 新的一行應該與上一行同一級別表達式的開頭處對齊。
  • 如果以上規則導致你的代碼混亂或者使你的代碼都堆擠在右邊,那就代之以縮進 8個空格。
  • 參數注釋時當注明其取值范圍等。
  • 返回值當注釋出失敗、錯誤、異常時的返回情況。
  • 異常當注釋出什麼情況、什麼時候、什麼條件下會引發什麼樣的異常。
/**
 * 一句話方法描述
 * 方法詳細描述
 * @param 參數名 參數描述
 * @param 參數名2 參數描述
 * @return 返回值類型說明
 * @throws Exception 異常說明
 * @see 類/方法/成員
*/

4.2.3 類成員變量和常量注釋

成員變量和常量需要使用javadoc形式的注釋,以說明當前變量或常量的含義。

/**
 * 成員變量描述
 */
private String test;

/** 成員變量描述 */
private int hello;

5 聲明

5.1 每行聲明變量的數量

推薦一行一個聲明,因為這樣以利於寫注釋。

int level;	 // indentation level
int size;        // size of table

注意:上面的例子中,在類型和標識符之間放了一個空格,另一種被允許的替代方式是使用制表符。

int		level;		// indentation level
int		size;		// size of table
char		username;	// username

5.2 初始化

盡量在聲明局部變量的同時初始化。唯一不這麼做的理由是變量的初始值依賴於某些先前發生的計算。

5.3 布局

只在代碼塊的開始處聲明變量(一個塊是指任何被包含在大括號“{“和”}“中間的代碼)。不要在首次用到該變量時才聲明之。這會把注意力不集中的程序員搞糊塗,同時會妨礙代碼在該作用域內的可移植性。

void myMethod() {
int int1 = 0;		// beginning of method block

    if (condition) {
        int int2 = 0;	// beginning of “if” block
        ...
    }
}

避免聲明的局部變量覆蓋上一級聲明的變量。例如,不要在內部代碼塊中聲明相同的變量名:

int count;
...
myMethod() {
    if (condition) {
        int count = 0;     // 避免
        ...
   }
   ...
}

5.4 類和接口的聲明

當編寫類和接口是,應該(強制)遵守以下格式規則:

  • 在方法名與其參數列表之前的左括號”(“間不要有空格。
  • 左大括號”{“位於聲明語句同行的末尾。
  • 右大括號”}“另起一行,與相應的聲明語句對齊。除非是一個空語句,”}“應緊跟在”{“之後。
  • 方法與方法之間以空行分隔。
class Sample extends Object {
    int ivar1;
    int ivar2;

    Sample(int i,	int j) {
        ivar1 = i;
        ivar2 = j;
    }

    int emptyMethod() {}
    ...
}

6 語句

6.1 簡單語句

每行之多包含一條語句,例如:

    argv++;     	// 推薦
    argc--;     	// 推薦
    argv++; argc--; 	// 避免

6.2 復合語句

復合語句是包含在大括號中的語句序列,形如”{ 語句 }“。例如下面各段。

  • 被括其中的語句應該較之復合語句縮進一個層次。
  • 左大括號”{“應位於復合語句起始行的行尾,右大括號”}“應另起一行並與復合語句首行對齊。
  • 大括號可以被用於所有語句,包括單個語句,只要這些語句是諸如 if-else 或 for 控制結構的一部分。這樣便於添加語句而無需擔心由於忘了加括號而引入 bug。

6.3 返回語句

一個帶返回值的return語句不使用小括號”()“,除非它們以某種方式使返回值更為顯見。例如:

return;

return myDisk.size();                  // 避免

return (size ? size : defaultSize);    // 避免

6.4 if, if-else, if else-if else 語句

if-else語句應該具有如下格式:

if (condition) {
    statements;
}

if (condition) {
    statements;
} else {
    statements;
}

if (condition) {
    statements;
} else if (condition) {
    statements;
} else{
    statements;
}

注意:if語句總是用”{“和”}“括起來,避免使用如下容易引起錯誤的格式:

if (condition)  // 避免
    statement;

6.5 for 語句

一個for語句應該具有如下格式:

for (initialization; condition; update) {
    statements;
}

當在for語句的初始化或更新子句中使用逗號時,避免因使用三個以上變量,而導致復雜度提高。若需要,可以在for循環之前(為初始化子句)或for循環末尾(為更新子句)使用單獨的語句。

6.6 while 語句

一個while語句應該具有如下格式:

while (condition) {
    statements;
}

6.7 do-while 語句

do {
    statements;
} while (condition);

6.8 switch 語句

一個switch語句應該具有如下格式:

switch (condition) {
    case ABC:
        statements;
        /* falls through */
    case DEF:
        statements;
        break;

    case XYZ:
        statements;
        break;

    default:
        statements;
        break;
}

每當一個case順著往下執行時(因為沒有break語句),通常應在break語句的位置添加注釋。上面的示例代碼中就包含注釋/* falls through */。

6.9 try-catch 語句

一個try-catch語句應該具有如下格式:

try {
    statements;
} catch (ExceptionClass	e) {
    statements;
}

try {
    statements;
} catch (ExceptionClass	e) {
    statements;
} finally {
    statements;
}

7 空白

7.1 空行

空行將邏輯相關的代碼段分隔開,以提高可讀性。下列情況應該總是使用空行:

  • 一個源文件的兩個片段(section)之間。
  • 類聲明和接口聲明之間。
  • 兩個方法之間。
  • 方法內的局部變量和方法的第一條語句之間。
  • 塊注釋或單行注釋之前。
  • 一個方法內的兩個邏輯段之間,用以提高可讀性。

7.2 空格

下列情況應該使用空格:

  • 一個緊跟著括號的關鍵字應該被空格分開。例如:
    while (true) {
        ...
    }
  • 空白應該位於參數列表中逗號的後面。
  • 所有的二元運算符,除了”.”,都應該使用空格將之與操作數分開。一元操作符和操作數之間不應該加空格,比如:負號(“-”)、自增(“++”)和自減(“–”)。例如:
    a += c + d;
    a = (a + b) / (c * d);
    
    while (d++ = s++) {
        n++;
    }
  • for語句中的表達式應該被空格分開。
    for (expr1; expr2; expr3)
  • 強制轉型後應該跟一個空格。
  • myMethod((byte) aNum, (Object) x);
    myMethod((int) (cp + 5), ((int) (i + 3)) + 1);

8 命名規范

命名規范使程序更易讀,從而更易於理解。它們也可以提供一些有關標識符功能的信息,以助於理解代碼。

8.1 包命名

包名由全部小寫字母組成,包名的前綴以com開頭,包名後續部分的格式為:

[域名反轉].[項目名].[模塊名].[子模塊名]…

例如:com.android.sim.message.sms

8.2 類和接口命名

類名是個一名詞,采用大小寫混合的方式,每個單詞的首字母大寫。盡量使你的類名簡潔而富於描述。使用完整單詞,或約定成俗並且使用廣泛的縮寫詞,如url,html,接口和類名規則一至但要使用I前綴。

繼承自系統組件類的命名,後綴必須明確表示出系統組件的類別,Activity類後綴使用Activity,Service類後綴使用Service,BroadcaseReceiver類後綴使用Receiver,ContentProvider使用Provider。

8.3 方法命名

方法名是一個動詞或者動名詞結構,采用大小寫混合的方式,第一個單詞的首字母小寫,其後單詞的首字母大寫,即駝峰命名規則。

以它做什麼來命名,而不是以它怎樣做命名。如doUpdate(),isNumber()。

8.4 變量命名

第一個單詞的首字母小寫,其後單詞的首字母大寫。變量名不應以下劃線或美元符號開頭,盡管這在語法上是允許的。變量名的選用應該易於記憶,即,能夠指出其用途。盡量避免單個字符的變量名,除非是一次性的臨時變量。臨時變量通常被取名為 i,j,k,m 和 n,它們一般用於整型;c,d,e,它們一般用於字符型。

其中系統控件中在後綴中體現控件類型,如下所示:

組件名稱 簡寫 組件名稱 簡寫 Button Btn RadioButton Rbtn ImageButton Ibtn TextView Tv ImageView Iv ListView Lv ProgressBar Pbar EditText Et ScrollView Sv CheckBox Cb RelativeLayout Rly LinearLayout Lly TableLayout Tly LinearLayout Aly FrameLayout Fly    

非 public 的、非 static 的字段名稱以 m 開頭。

static 字段名稱以 s 開頭。

其它字段以小寫字母開頭。

public	class MyClass {
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

8.5 常量命名

類常量的聲明,應該全部大寫,單詞間用下劃線隔開。

static final int MIN_WIDTH = 4;
static final int MAX_WIDTH = 999;
static final int GET_THE_CPU = 1;

8.6 異常命名

自定義異常的命名必須以Exception為結尾,已明確標示為一個異常。

異常實例一般使用e、ex等,在多個異常時使用該異常名或簡寫加E,Ex等組成,如:SQLEx,ActionEx。

8.7 Layout 命名

命名必須以全部單詞小寫,單詞間以下劃線分割,並且盡可能的使用名詞或名詞組,即使用 模塊名_功能名稱 來命名。

addressbook_list.xml	// 推薦
list_addressbook.xml	// 避免

8.8 資源 ID 命名

layout中所使用的id命名必須以全部單詞小寫,單詞間以下劃線分割,並且盡可能的使用名詞或名詞組,並且要求能夠通過id直接理解當前組件要實現的功能。

EditText名 @+id/book_name_edit	// 推薦
EditText名 @+id/textbookname	// 避免

8.9 Activity 中 View 命名

采用大小寫混合模式,第一個單詞首字母小寫,其余單詞首字母大寫最後一個單詞為該View 類型的縮寫,格式如下:

邏輯名+View 類型縮寫(View 縮寫參照 8.4 組件名稱縮寫表)。

Button homeBtn

8.10 strings.xml 中 ID 命名

命名必須以全部單詞小寫,單詞間以下劃線分割,並且盡可能的使用名詞或名詞組,格式如下:

  • activity名稱_功能模塊名稱_邏輯名稱 或
  • activity名稱_邏輯名稱 或
  • common_邏輯名稱
邏輯名稱多個單詞用下劃線連接,同時使用activity名稱注釋。
main_menu_about
main_title
common_exit
common_app_name

8.11 資源命名

layout中使用的所有資源(如drawable,style等)命名必須以全部單詞小寫,單詞間以下劃線分割,並且盡可能的使用名詞或名詞組,即使用模塊名_用途來命名。如果為公共資源,如分割線等,則直接用用途來命名。如:menu_icon_navigate.png

9 編程規范

9.1 單位規范

在使用單位時,如果沒有特殊情況,一律使用 sp 作為文字大小的單位,將 dip 作為其他元素的單位。因為這兩個單位是與設備分辨率無關的,能夠解決在不同分辨率的設備上顯示效果不同的問題。另外,在編碼中定義控件的 margin 或 padding 屬性時,SDK 裡面並沒有提供 dip 單位的 api 設置接口,而是提供了默認的 px 設置。

Button btn = new Button(context);
LayoutParams lp = new
        LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
lp.setMargins(0, 0, 0, 0); 
btn.setTextSize(12); 
btn.setPadding(0, 0, 0, 0);

這個時候,一般在設置 margin 和 padding 時,應該對要設置的 dip 值轉換為 px 單位,而字體的大小設置中,系統默認給出了 sp 的單位,所以可以不用進行轉換。轉換的方法參考下面的代碼:

/**
 * 把dip單位轉成px單位
 * @param context context對象
 * @param dip dip數值
 * @return dip對應的px值
 */
public static int formatDipToPx(Context context, int dip) {
    DisplayMetrics dm = new DisplayMetrics();
    ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
    int dip = (int) Math.ceil(dip * dm.density);
    return dip;
}

9.2 引用類變量和類方法

避免用一個對象訪問一個類的靜態變量和方法。應該用類名替代。

classMethod();               // 推薦
AClass.classMethod();        // 推薦
anObject.classMethod();      // 避免

9.3 常量

位於 for 循環中作為計數器值的數字常量,除了-1,0 和 1 之外,不應被直接寫入代碼。

9.4 變量賦值

避免在一個語句中給多個變量賦相同的值,它很難讀懂。

9.5 信令類

如果類只是用來作為信息傳遞的中間變量,則應該聲明為信令類,即所有的全局變量都是 final 類型,在初始化時賦值。

private final String name;
public Foo(String str) {
    name = str;
}
public Foo(String str ) {
    this.str = str;                   // 避免在構造函數中出現this引用
}

9.6 不要忽略異常

有時,完全忽略異常是非常誘人的。

void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) { }    // 錯誤
}

絕對不要這麼做。也許你會認為:你的代碼永遠不會碰到這種出錯的情況,或者處理異常並不重要,可類似上述忽略異常的代碼將會在代碼中埋下一顆地雷,說不定哪天它就會炸到某個人了。你必須在代碼中以某種規矩來處理所有的異常。根據情況的不同,處理的方式也會不一樣。可接受的替代方案包括(按照推薦順序):向方法的調用者拋出異常。

void setServerPort(String value) throws NumberFormatException {
    serverPort = Integer.parseInt(value);
}

根據抽象級別拋出新的異常。

void setServerPort(String value) throws ConfigurationException {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new ConfigurationException("Port " + value + " is not valid.");
    }
}

默默地處理錯誤並在 catch {} 語句塊中替換為合適的值。

/**	設置端口。假如值不是數字則用80代替 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    }	catch (NumberFormatException e) {
        serverPort = 80; 	// 服務默認端口
    }
}

捕獲異常並拋出一個新的 RuntimeException。這種做法比較危險:只有確信發生該錯誤時最合適的做法就是崩潰,才會這麼做。

/**	設置端口,假如值不是數字則程序終止。 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new RuntimeException("port " + value " is invalid, ", e);
    }
}

請記住,最初的異常是傳遞給構造方法的 RuntimeException。如果代碼必須在 Java 1.3 版本下編譯,需要忽略該異常。最後一招:如果確信忽略異常比較合適,那就忽略吧,但必須把理想的原因注釋出來。

/**	假如值不是數字則使用原來的端口號。 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        // 方法記錄:無視無效的用戶輸入。
        // 服務端口不會被改變。
    }
}

9.7 不要捕獲頂級的 Exception

有時在捕獲 Exception 時偷懶也是很吸引人的,類似如下的處理方式:

try {
    someComplicatedIOFunction();       	// 可能拋出IOException
    someComplicatedParsingFunction();  	// 可能拋出ParsingException
    someComplicatedSecurityFunction(); 	// 可能拋出SecurityException
    // 其他可以拋出Exception的代碼
} catch (Exception e) {                	// 一次性捕獲所有exceptions
    handleError();                     	// 只有一個通用的處理方法!
}

不要這麼做。絕大部分情況下,捕獲頂級的 Exception 或 Throwable 都是不合適的,Throwable 更不合適,因為它還包含了 Error 異常。這種捕獲非常危險。這意味著本來不必考慮的 Exception(包括類似 ClassCastException 的 RuntimeException)被卷入到應用程序級的錯誤處理中來。這會讓代碼運行的錯誤變得模糊不清。這意味著,假如別人在你調用的代碼中加入了新的異常,編譯器將無法幫助你識別出各種不同的錯誤類型。絕大部分情況下,無論如何你都不應該用同一種方式來處理各種不同類型的異常。

本規則也有極少數例外情況:期望捕獲所有類型錯誤的特定的測試代碼和頂層代碼(為了阻止這些錯誤在用戶界面上顯示出來,或者保持批量工作的運行)。這種情況下可以捕獲頂級的 Exception(或 Throwable)並進行相應的錯誤處理。在開始之前,你應該非常仔細地考慮一下,並在注釋中解釋清楚為什麼這麼做是安全的。

比捕獲頂級 Exception 更好的方案:

  • 分開捕獲每一種異常,在一條 try 語句後面跟隨多個 catch 語句塊。這樣可能會有點別扭,但總比捕獲所有 Exception 要好些。請小心別在 catch 語句塊中重復執行大量的代碼。
  • 重新組織一下代碼,使用多個 try 塊,使錯誤處理的粒度更細一些。把 IO 從解析內容的代碼中分離出來,根據各自的情況進行單獨的錯誤處理。
  • 再次拋出異常。很多時候在你這個級別根本就沒必要捕獲這個異常,只要讓方法拋出該異常即可。

請記住:異常是你的朋友!當編譯器指出你沒有捕獲某個異常時,請不要皺眉頭。而應該微笑:編譯器幫助你找到了代碼中的運行時(runtime)問題。

9.8 不要使用 Finalizer

Finalizer 提供了一個機會,可以讓對象被垃圾回收器回收時執行一些代碼。

優點:便於執行清理工作,特別是針對外部資源。

缺點:調用 finalizer 的時機並不確定,甚至根本就不會調用。

結論:我們不要使用 finalizers。大多數情況下,可以用優秀的異常處理代碼來執行那些要放入 finalizer 的工作。如果確實是需要使用 finalizer,那就定義一個 close() 方法(或類似的方法),並且在文檔中准確地記錄下需要調用該方法的時機。相關例程可以參見InputStream。這種情況下還是適合使用 finalizer 的,但不需要在 finalizer 中輸出日志信息,因為日志不能因為這個而被撐爆。

9.9 使用完全限定 Import

當需要使用 foo 包中的 Bar 類時,存在兩種可能的 import 方式:

1. import foo.*;

優點:可能會減少 import 語句。

2. import foo.Bar;

優點:實際用到的類一清二楚。代碼的可讀性更好,便於維護。

結論: 用後一種寫法來 import 所有的 Android 代碼。不過導入 java 標准庫 (java.util.*、java.io.*等) 和單元測試代碼 (junit.framework.*) 時可以例外。

9.10 對 Import 語句排序

import 語句的次序應該如下:

1. Android imports

2. 第三方庫(com、junit、net、org)

3. java 和 javax

為了精確匹配 IDE 的配置,import 順序應該是:

1. 在每組內部按字母排序,大寫字母排在小寫字母的前面。

2. 每個大組之間應該空一行(android、com、junit、net、org、java、javax)。

3. 原先次序是不作為規范性要求的。這意味著要麼允許 IDE 改變順序,要麼使用 IDE的開發者不得不禁用 import 自動管理功能並且人工維護 import。這看起來比較糟糕。每當說起 java 規范,推薦的規范到處都是。符合我們要求的差不多就是“選擇一個次序並堅持下去。”於是,我們就選擇一個規范,更新規范手冊,並讓 IDE去遵守它。我們期望:不必耗費更多的精力,用 IDE 編碼的用戶就按照這種規則去 import 所有的 package。

基於以下原因,選定了本項規則:

  • 導入人員期望最先看到的放在最開始位置(android)。
  • 導入人員期望最後才看到的放在最後(java)。
  • 風格讓人容易遵守。
  • IDE 可以遵守。

靜態 import 的使用和位置已經成為略帶爭議的話題。有些人願意讓靜態 import 和其它 import 混在一起,另一些人則期望讓它們位於其它 import 之上或者之下。另外,我們還未提到讓所有 IDE 都遵守同一個次序的方法。

因為大多數人都認為這部分內容並不要緊,只要遵守你的決定並堅持下去即可。

9.11 限制變量的作用范圍

局部變量的作用范圍應該是限制為最小的(Effective Java 第 29 條)。使用局部變量,可以增加代碼的可讀性和可維護性,並且降低發生錯誤的可能性。每個變量都應該在最小范圍的代碼塊中進行聲明,該代碼塊的大小只要能夠包含所有對該變量的使用即可。

應該在第一次用到局部變量的地方對其進行聲明。幾乎所有局部變量聲明都應該進行初始化。如果還缺少足夠的信息來正確地初始化變量,那就應該推遲聲明,直至可以初始化為止。

本規則存在一個例外,就是涉及 try-catch 語句的情況。如果變量是用方法的返回值來初始化的,而該方法可能會拋出一個 checked 異常,那麼必須在 try 塊中進行變量聲明。如果需在 try 塊之外使用該變量,那它就必須在 try 塊之前就進行聲明了,這時它是不可能進行正確的初始化的。

//	實例化類cl,表示有序集合
Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

//	集合練習
s.addAll(Arrays.asList(args));

但即便是這種情況也是可以避免的,把try-catch 塊封裝在一個方法內即可。

Set createSet(Class cl) {
//	實例化類cl,表示有序集合
try {
    return (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible"); 
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable"); 
    }
}
...

//	集合練習
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

除非理由十分充分,否則循環變量都應該在for語句內進行聲明。

for (int i = 0; i n; i++) {
    doSomething(i);
}
for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

9.12 使用標准的 Java Annotation

Annotation 應該位於 Java 語言元素的其它修飾符之前。 簡單的 marker annotation(@Override 等)可以和語言元素放在同一行。 如果存在多個 annotation,或者annotation 是參數化的,則應按字母順序各占一行來列出。

對於 Java 內建的三種 annotation,Android 標准的實現如下:

  • @Deprecated:只要某個語言元素已不再建議使用了,就必須使用@Deprecated annotation。如果使用了@Deprecated annotation,則必須同時進行@deprecated Javadoc 標記,並且給出一個替代的實現方式。此外請記住,被@Deprecated 的方法仍然是能正常執行的。如果看到以前的代碼帶有@deprecated Javadoc 標記,也請加上@Deprecated annotation。
  • @Override:只要某個方法覆蓋了已過時的或繼承自超類的方法,就必須使用@Override annotation。例如,如果方法使用了@inheritdocs Javadoc 標記,且繼承自超類(而不是 interface),則必須同時用@Override 標明覆蓋了父類方法。
  • @SuppressWarnings:@SuppressWarnings annotation 僅用於無法消除編譯警告的場合。如果警告確實經過測試“不可能消除”,則必須使用@SuppressWarnings annotation,以確保所有的警告都能真實反映代碼中的問題。

當需要使用@SuppressWarnings annotation時,必須在前面加上TODO注釋行,用於解釋“不可能消除”警告的條件。通常是標明某個令人討厭的類用到了某個拙劣的接口。

// TODO:	第三方類 com.third.useful.Utility.rotate()	必須采用泛型
@SuppressWarnings("generic-cast")
List blix = Utility.rotate(blax);

如果需要使用@SuppressWarnings annotation,應該重新組織一下代碼,把需要應用 annotation 的語言元素獨立出來。

9.13 簡稱等同於單詞

簡稱和縮寫都視為變量名、方法名和類名。以下名稱可讀性更強:

好 差 XmlHttpRequest XMLHTTPRequest getCustomerId getCustomerID class Html class HTML String url String URL long id long ID

如何對待簡稱,JDK 和 Android 底層代碼存在很大的差異。因此,你幾乎不大可能與其它代碼取得一致。別無選擇,把簡稱當作完整的單詞看待吧。

關於本條規則的進一步解釋,請參閱 Effective Java 第 38 條和 Java Puzzlers 第 68條。

9.14 使用 TODO 注釋

對那些臨時性的、短期的、夠棒但不完美的代碼,請使用 TODO 注釋

TODO 注釋應該包含全部大寫的 TODO,後跟一個冒號:

// TODO: Remove this code after the UrlTable2 has been checked in. 
// TODO: Change this to use a flag instead of a constant.

如果 TODO 注釋是“將來要做某事”的格式,則請確保包含一個很明確的日期(“在2013 年 11 月會修正”),或是一個很明確的事件(“在所有代碼整合人員理解了 V7 協議之後刪除本段代碼”)。

9.15 慎用 Log

記錄日志會對性能產生顯著的負面影響。如果日志內容不夠簡煉的話,很快會喪失可用性。日志功能支持五種不同的級別。以下列出了各個級別及其使用場合和方式。

  • ERROR: 該級別日志應該在致命錯誤發生時使用,也就是說,錯誤的後果能被用戶看到,但是不明確刪除部分數據、卸裝程序、清除數據區或重新刷機(或更糟糕)就無法恢復。該級別總是記錄日志。需要記錄 ERROR 級別日志的事件一般都應該向統計信息收集(statistics-gathering )服務器報告。
  • WARNING:該級別日志應該用於那些重大的、意外的事件,也就是說,錯誤的後果能被用戶看到,但是不采取明確的動作可能就無法無損恢復,從等待或重啟應用開始,直至重新下載新版程序或重啟設備。該級別總是記錄日志。需記錄WARNING 級別日志的事件也可以考慮向統計信息收集服務器報告。
  • INFORMATIVE:該級別的日志應該用於記錄大部分人都會感興趣的事件,也就是說,如果檢測到事件的影響面可能很廣,但不一定是錯誤。應該只有那些擁有本區域內最高級別身份認證的模塊才能記錄這些日志(為了避免級別不足的模塊重復記錄日志)。該級別總是記錄日志。
  • DEBUG:該級別的日志應該用於進一步記錄有關調查、調試意外現象的設備事件。應該只記錄那些有關控件運行所必需的信息。如果 debug 日志占用了太多的日志空間,那就應該使用詳細級別日志(verbose)才更為合適。即使是發行版本(release build),該級別也會被記錄,並且需用 if (LOCAL_LOG) 或 if (LOCAL_LOGD) 語句塊包裹,這裡的LOCAL_LOG[D] 在你的類或子控件中定義。這樣就能夠一次性關閉所有的調試日志。因此在 if (LOCAL_LOG) 語句塊中不允許存在邏輯判斷語句。所有日志所需的文字組織工作也應在 if (LOCAL_LOG) 語句塊內完成。如果對記錄日志的調用會導致在 if (LOCAL_LOG) 語句塊之外完成文字組織工作,那該調用就必須控制在一個方法內完成。還存在一些代碼仍然在使用 if (localLOGV)。這也是可以接受的,雖然名稱不是標准的。
  • VERBOSE:該級別日志應用於所有其余的事件。該級別僅會在調試版本(debug build)下記錄日志,並且需用 if (LOCAL_LOGV) 語句塊(或等效語句)包裹,這樣該部分代碼默認就不會編譯進發行版本中去了。所有構建日志文字的代碼將會在發行版本中剝離出去,並且需包含在 if (LOCAL_LOGV) 語句塊中。

    注意:

  • 除了 VERBOSE 級別外,在同一個模塊中同一個錯誤應該盡可能只報告一次:在同一個模塊內的一系列層層嵌套的函數調用中,只有最內層的函數才返回錯誤;並且只有能為解決問題提供明顯幫助的時候,同一模塊中的調用方才寫入一些日志。
  • 除了 VERBOSE 級別外,在一系列嵌套的模塊中,當較低級別的模塊對來自較高級別模塊的非法數據進行檢測時,應該只把檢測情況記錄在 DEBUG 日志中,並且只記錄那些調用者無法獲取的信息。特別是不需要記錄已經拋出異常的情況(異常中應該包含了全部有價值的信息),也不必記錄那些只包含錯誤代碼的信息。當應用程序與系統框架間進行交互時,這一點尤為重要。系統框架已能正確處理的第三方應用程序,也不應該記錄大於 DEBUG 級別的日志。僅當一個模塊或應用程序檢測到自身或來自更低級別模塊的錯誤時,才應該記錄 INFORMATIVE 及以上級別的日志。
  • 如果一個通常要記錄日志的事件可能會多次發生,則采取一些頻次限制措施或許是個好主意,以防日志被很多重復(或類似)的信息給撐爆了。
  • 網絡連接的丟失可被視為常見現象,也是完全可以預見的,不應該無緣無故就記錄進日志。影響范圍限於應用程序內部的網絡中斷應該記錄在 DEBUG 或 VERBOSE級別的日志中(根據影響的嚴重程度及意外程度,再來確定是否在發行版本中也記錄日志)。
  • 有權訪問的文件系統或第三方應用程序發起的系統空間滿,應該記錄大於INFORMATIVE 級別的日志。
  • 來自任何未授信源的非法數據(包括共享存儲上的任何文件,或來自任何網絡連接的數據)可被視為可預見的,如果檢測到非法數據也不應該記錄大於 DEBUG 級別的日志(即使記錄也應盡可能少)。
  • 請記住,對字符串使用+操作符時,會在後台以默認大小(16 個字符)緩沖區創建一個 StringBuilder 對象,並且可能還會創建一些其它的臨時 String 對象。換句話說,顯式創建 StringBuilders 對象的代價並不會比用'+'操作符更高(事實上效率還將會提高很多)。還要記住,即使不會再去讀取這些日志,調用 Log.v() 的代碼也將編譯進發行版中並獲得執行,包括創建字符串的代碼。
  • 所有要被人閱讀並存在於發行版本中的日志,都應該簡潔明了、沒有秘密、容易理解。這裡包括所有 DEBUG 以上級別的日志。
  • 只要有可能,日志就應該一句一行。行長最好不超過 80 或 100 個字符,盡可能避免超過 130 或 160 個字符(包括標識符)的行。
  • 報告成功的日志記錄絕不應該出現在大於 VERBOSE 級別的日志中。
  • 用於診斷難以重現事件的臨時日志應該限於 DEBUG 或 VERBOSE 級別,並且應該用 if 語句塊包裹,以便在編譯時能夠一次全部關閉。
  • 小心日志會洩漏隱私。應該避免將私人信息記入日志,受保護的內容肯定也不允許記錄。這在編寫系統框架級代碼時尤為重要,因為很難預知哪些是私人信息和受保護信息。
  • 絕對不要使用 System.out.println()(或本地代碼中的 printf())。System.out 和System.err 會重定向到/dev/null,因此 print 語句不會產生任何可見的效果。可是,這些調用中的所有字符串創建工作都仍然會執行。
日志的黃金法則是:你的日志記錄不會導致其它日志的緩沖區溢出,正如其他人的日志也不會讓你的溢出一樣。

10 在Eclipse中使用模板

10.1 導入android編碼規范模板

在eclipse的preferences中,選擇java → code style → formatter中選擇Import,選擇工程根目錄下的development/ide/eclipse/目錄下的android-formatting.xml。

在eclipse的preferences中,選擇java → code style → Organize Imports中選擇Import,選擇工程根目錄下的development/ide/eclipse/目錄下的android.importorder。

說明:導入這兩個文件,是為了與源碼中的Android程序保持一致的編碼規范。android-formatting.xml用來配置eclipse編譯器的代碼風格;android.importorder用來配置eclipse的import的順序和結構。

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved