貢獻者適用的 AOSP Java 程式碼樣式

本頁中的程式碼樣式是嚴格規則,用於將 Java 程式碼提供給 Android 開放原始碼計畫 (AOSP)。對 Android 平台的貢獻 違反這些規定通常無法接受。三 並非所有現有程式碼都遵循這些規則,但我們希望 以符合相關規定的新程式碼請參閱尊重程式設計 。

保持一致

其中最簡單的規則之一就是「持之以恆」。如要編輯程式碼,請 查看周圍程式碼,並確定樣式。如果這樣 程式碼也應該在 if 子句前後加上空格。如果程式碼 評論周圍有小星方塊 留下你的評論 周圍也有幾顆星星。

要遵循樣式準則的重點,在於要有共通性 不必費心編寫程式 你說什麼。這裡說明通用的樣式規則 雖然當地風格也很重要如果您新增的程式碼 與檔案周圍的現有程式碼有極大差異 讀者在閱讀時就會離開節奏請嘗試 避免這種情況發生

Java 語言規則

Android 遵循標準 Java 編碼慣例,另有其他規則 。

不要忽略例外狀況

您可能會很想撰寫忽略例外狀況的程式碼,例如:

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

請不要這麼做。雖然您可能以為程式碼不會發生這類情況 或根本沒有處理錯誤的狀況,而忽略這類型的 例外狀況會在您的程式碼中建立礦場,讓其他人 觸發一次您必須處理程式碼中的所有例外狀況, 原則上實際處理方式取決於個案情況。

「任何人都應該要有空白的捕獲子句,而且不應該好奇心。 感覺。有時確實是正確的 但至少得想著想在 Java 中,您無法 逃離令人毛骨悚然的感覺。」- 大明 哥斯林

可接受的替代方案如下:

  • 將例外狀況向上擲回方法的呼叫端。
      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 {} 區塊。
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
  • 擷取例外狀況並擲回新的 RuntimeException 例項。 這很危險,因此除非你認為正是因為如此,否則這麼做 錯誤發生時,應當異常終止。
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    敬上
  • 最後,如果您確信忽略例外情況是 可以不理會,但您也必須註明原因 。
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }

不要擷取一般例外狀況

擷取例外狀況時可能會忍受延遲 如下所示:

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

請不要這麼做。大多數情況下,找出一般性 Exception 或 Throwable (最好不是 Throwable 因為其中包含 Error 例外狀況)。這屬於危險性 從未預期的情況 (包括 ClassCastException 等執行階段例外狀況) 會在應用程式層級的錯誤處理中抓出。它會掩蓋故障 如何處理程式碼屬性,亦即有人加入新的 您呼叫的程式碼有例外狀況,編譯器不會指出 您必須以不同方式處理錯誤大多數的情況下 不得以相同的方式處理不同類型的例外狀況。

這項規則的罕見情形是測試程式碼和頂層程式碼,其中 您希望找出所有類型的錯誤 (防止顯示 或持續執行批次工作)。在這些情況下 一般 Exception (或 Throwable),並妥善處理錯誤。 不過,在這之前請三思,並撰寫評論 解釋為何應該如此安全

擷取一般例外狀況的替代方案:

  • 從多重貓咪區塊中分別擷取每個例外狀況,例如:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • 重構程式碼,進行更精細的錯誤處理, 多次嘗試方塊。將 IO 從剖析中分割並處理錯誤 來單獨使用。
  • 擲回例外狀況。許多時候,您甚至可以用 例外狀況,只要讓方法擲回即可。

請記住,你的朋友是例外情況!當編譯器表明 未接收到例外狀況,也不要用草球。微笑!編譯器剛剛成功建立 輕鬆找出程式碼中的執行階段問題

請勿使用最終化器

當物件 我們計算了垃圾收集機制雖然定案影片適合用來清理 (尤其是外部資源),無法保證 將呼叫最終器 (或者甚至系統完全呼叫)。

Android 不會使用最終化器。在大部分的情況下,您可以使用 但這是最佳的例外狀況處理方法如果絕對需要最終化器 定義 close() 方法 (或類似方式),並明確記錄該方法 方法需要呼叫 (詳情請參閱 InputStream)。在本例中 是可以提供,但並非必須列印簡短記錄訊息 。

完全符合資格的匯入項目

如果您要使用套件 foo 中的 Bar 類別,有兩個 可能的匯入方式

  • import foo.*;

    可能會減少匯入陳述式的數量。

  • import foo.Bar;

    清楚指明所使用的類別,程式碼則更加明確 以便維護人員讀取

使用 import foo.Bar; 匯入所有 Android 程式碼。一個 為 Java 標準程式庫 (java.util.*、 java.io.* 等) 和單元測試程式碼 (junit.framework.*)。

Java 程式庫規則

使用 Android 的 Java 程式庫和工具有一些慣例。於 有一些案例時,慣性的變化,在舊版程式碼中, 可能會使用已淘汰的模式或程式庫使用這類程式碼時 您可以繼續使用現有樣式建立新元件時 但一律不得使用已淘汰的程式庫

Java 樣式規則

使用 Javadoc 標準註解

每個檔案的上方都應該有版權聲明,緊接在 後面加上 套件和 import 陳述式 (每個區塊均以空白行分隔) 最後是類別或介面宣告在 Javadoc 註解中 說明類別或介面的作用

/*
 * Copyright (C) yyyy The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}
敬上

您編寫的每個類別和非常見公開方法都必須包含 Javadoc 註解,至少包含一個句子來說明類別 或方法採用何種做法這個句子應以第三人稱 敘述性動詞

範例

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

或

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

您不需要為簡易的 get 和設定如 setFoo()如果您所有的 Javadoc 都顯示為「sets Foo」。如果 方法能夠執行更複雜的工作 (例如強制執行限制 或是有重要副作用),就必須加以記錄如果不是 明顯地產出「Foo」這個房源所以請務必記錄下來

無論您的編寫方式是公開或其他方式,都會受益於 Javadoc。 公開方法屬於 API 的一部分,因此需要使用 Javadoc。Android 版 在編寫 Javadoc 時不會強制執行特定樣式 新增註解,但請按照如何為 Javadoc 工具撰寫文件註解一文的說明。

寫出簡短方法

如果可以,請盡量使用精簡且聚焦的方法。我們瞭解 方法有時候可行,因此這個方法沒有硬性限制 長度。如果方法超過 40 行,請先思考能否 不受程式結構影響

定義標準位置中的欄位

定義欄位,可以是檔案頂端或緊接在 加以運用

限制變數範圍

請盡量減少本機變數的範圍。這個 程式碼的可讀性和可維護性,並減少 出錯的可能性請在最內宣告每個變數 包含該變數的所有使用區塊

在第一次使用時宣告本機變數。 幾乎每個本機變數宣告都應包含一個初始化器。 如果資訊不足,無法初始化變數 請謹慎地將聲明延後到執行為止。

例外狀況是 try-catch 陳述式。如果變數是以 擲回已檢查例外狀況的方法的回傳值,必須 會在 try 區塊中初始化如果這個值必須在 則在 try 區塊之前宣告此函式,而應在該區塊之前宣告 但無法合理初始化:

// Instantiate class cl, which represents some sort of Set

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");
}

// Exercise the set
s.addAll(Arrays.asList(args));

不過,透過封裝 try-catch 方法 封鎖在方法中

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

除非在 陳述式中宣告迴圈變數,除非 理由如下:

for (int i = 0; i < n; i++) {
    doSomething(i);
}

和

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

訂單匯入陳述式

匯入陳述式的順序為:

  1. Android 匯入
  2. 從第三方匯入 (com、junit、 net、org)
  3. 「java」和「javax」

如要完全符合 IDE 設定,匯入作業必須:

  • 每個群組內的字母順序,中間是大寫字母 大寫字母 (例如,在 a 前面)
  • 每個主要群組之間以空白行分隔 (android、com、junit、 net、org、java、 javax)

最初,順序並沒有樣式規定,意味著 IDE 就是總是改變排序,IDE 開發人員 停用自動匯入管理功能,並手動維護 以及匯入的內容這種情形已判定為不良。當要求 Java 式時, 喜歡的風格五花八門,因此 Android 裝置必須 「挑選訂單,並確保一致性」我們選擇了風格 更新了樣式指南,並讓 IDE 遵循指示。我們預期 IDE 使用者負責編寫程式碼,且所有套件中的匯入內容都會符合以下情況 您無須額外進行工程作業。

我們選擇的風格如下:

  • 使用者最想要優先查看的匯入項目通常位於頂端 (android)。
  • 使用者偏好查看的匯入內容通常會排在底部 (java)。
  • 人類可以輕鬆掌握風格。
  • IDE 可以遵循樣式。

將靜態匯入項目排在所有其他匯入項目上方,順序與 也不必定期匯入

使用空格進行縮排

我們針對區塊使用四 (4) 空格縮排,而不應使用定位點。如有疑問 與周圍程式碼一致

我們針對行換行使用八 (8) 空格,包括函式呼叫 和作業

建議

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

不建議使用

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

遵循欄位命名慣例

  • 非公開、非靜態欄位名稱的開頭是 m。
  • 靜態欄位名稱開頭為 s。
  • 其他欄位的開頭則是小寫英文字母。
  • 靜態最終欄位 (常數,可大幅變動) 為 ALL_CAPS_WITH_UNDERSCORES。

例如:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

使用標準大括號樣式

請將大括號放在程式碼前面,而不是單獨一行:

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

條件式陳述式前後必須使用大括號。例外狀況:如果 整個條件 (限制條件和主體) 合佔一行,您可以 可 (但不一定要) 在同一行。舉例來說 可接受:

if (condition) {
    body();
}

,這是可以接受的:

if (condition) body();

但這不在可接受的範圍內:

if (condition)
    body();  // bad!

限制行長度

程式碼中的每一行文字不得超過 100 個字元。 雖然許多討論已圍繞這項規則,但這項決定仍然存在 ,長度上限為 100 個字元, 例外狀況:

  • 如果註解行包含範例指令或詳細網址 超過 100 個字元,該行可超出 100 個字元 剪下及貼上。
  • 匯入線條可能會超過限制,因為很少見到 (這也能簡化工具的編寫方式)。

使用標準 Java 註解

註解應置於相同語言的其他修飾符之前 元素。簡易標記註解 (例如 @Override) 可列於 語言元素的同一行如有多則註解 或參數化註解, 依字母順序排列

Java 中三個預先定義註解的 Android 標準做法 是:

  • 使用 @Deprecated 註解 使用註解元素。如果您使用 @Deprecated 註解,也必須有 @deprecated Javadoc 標記,然後命名替代實作。此外, 請注意,@Deprecated 方法仍應可以運作。 如果您發現有 @deprecated Javadoc 標記的舊程式碼,請將 @Deprecated 註解。
  • 請一律使用 @Override 註解 方法會覆寫宣告或實作 父類別例如,如果您使用 @inheritdocs Javadoc 標記,且 衍生自類別 (非介面),您也必須加上註解 這個方法會覆寫父項類別的方法
  • 使用 @SuppressWarnings 註解 才可正常使用 取消警示如果警告通過 排除」測試,@SuppressWarnings 註解必須 確保所有警告反映出 再也不是件繁重乏味的工作

    需要 @SuppressWarnings 註解時 開頭為 TODO 註解,說明「不可」 排除」值。通常用來找出有問題的類別 提供奇怪的介面例如:

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);

    需要 @SuppressWarnings 註解時,請重構程式碼 這樣就能區隔出含有註解的軟體元素

將縮寫詞視為單字

在命名變數、方法 和類別讓名稱更清晰易讀:

良好 不佳
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
HTML 類別 HTML 類別
字串網址 字串網址
長 ID 長 ID

JDK 和 Android 程式碼集都不一致 因此幾乎不可能與 周圍的程式碼因此,請一律將縮寫字視為字詞。

使用 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 的格式為「日後要執行某項動作」沒問題 加入特定日期 (「修正期限為 2005 年 11 月」),或是 特定事件 (「在所有生產混音器的情況下移除此程式碼 瞭解通訊協定 V7」)。

節制記錄

雖然需要記錄功能,但會對 如未合理保持 terse。記錄功能提供五種不同的記錄等級:

  • ERROR:使用發生嚴重錯誤的情況時, 可能會讓使用者看見的後果,而且無法復原 而不需要刪除部分資料、解除安裝應用程式 抹除資料分區,或重新刷新裝置 (甚至更糟)。 系統一律會記錄這個等級。導致部分記錄發生的問題 ERROR 級適合回報給 收集統計資料的伺服器。
  • WARNING:使用時機嚴重或非預期的情況 也就是說,使用者一看就知道結果了 藉由執行 從等待或重新啟動應用程式 以便重新下載新版應用程式或重新啟動 裝置。系統一律會記錄這個等級。合理化記錄問題 WARNING層級就算是回報給 收集統計資料的伺服器。
  • INFORMATIVE:用來記下有意思的資訊 也就是說,如果偵測到 但這不一定是錯誤如此 只能以認為有利的模組記錄 是網域中最具公信力的資訊 (以避免重複 而非授權元件進行記錄) 事件。系統一律會記錄這個等級。
  • DEBUG:用來進一步注意 可能相關的裝置進行調查及偵錯 行為只記錄收集所需的資料 瞭解元件運作情形如果偵錯 則請使用詳細記錄 。

    即使發布子版本也會記錄這個等級,此等級為必要項目 由 if (LOCAL_LOG) 或 if LOCAL_LOGD) 區塊包圍,其中 LOCAL_LOG[D] 的定義如下 以便在類別或子元件中預測 停用所有這類記錄功能因此,沒有任何有效邏輯 在 if (LOCAL_LOG) 區塊中。各類字串的建構內容 此外,記錄檔中也需要放置在 if (LOCAL_LOG) 區塊。不要重構記錄呼叫 並輸出至方法呼叫 建立字串的建築物 if (LOCAL_LOG) 區塊。

    部分程式碼仍顯示 if (localLOGV)。這個 也可以接受,但並非標準名稱。

  • VERBOSE:用於其他用途。這個等級只有 記錄於偵錯版本上,且應該以 if (LOCAL_LOGV) 封鎖 (或同等元素), 編碼器-解碼器架構所有字串架構都會去除 以及發布子版本 if (LOCAL_LOGV) 區塊。

附註

  • 在特定模組 (不含 VERBOSE 層級) 中, 可能只能回報一次。在單一網路鏈中 函式呼叫時,只有最內部的函式 傳回錯誤,且相同模組中的呼叫端只能新增 或是記錄一些記錄,如果這對於找出問題會很有幫助。
  • 在 VERBOSE 層級的模組鏈結中, 較低層級模組會偵測來自較高層級模組的無效資料 較低層級的模組應該只將此情況記錄到 DEBUG 記錄,且僅適用於記錄未提供 否則可為呼叫端提供完整存取權具體來說 記錄擲回例外狀況的情境 (例外狀況應 包含所有相關資訊),或只在 包含在錯誤代碼中尤其是 架構與應用程式互動中相當重要的一環 以及第三方應用程式造成的損害 由架構處理的事件,觸發記錄不應高於 DEBUG等級。只有在發生此情況時,才會觸發記錄程序 INFORMATIVE 以上等級是指模組或應用程式偵測到 獨立層級或來自較低層級的錯誤
  • 當一個常用於合理化記錄功能的條件時 建議您執行 頻率限制機制,以避免含有許多 重複相同 (或非常類似) 資訊的副本
  • 網路連線中斷的情形相當常見,而且完全沒有網路連線。 請勿故意記錄。網路斷線 會影響應用程式內後果的連線 DEBUG 或 VERBOSE 層級 (取決於 結果嚴重到足以讓使用者留存在版本上 建構)。
  • 在可存取或應用程式的檔案系統中擁有完整的檔案系統 代表第三方應用程式不應在層級記錄 大於 INFORMATIVE 的
  • 來自任何不受信任的來源 (包括 共用儲存空間或透過網路 安全連線) 視為預期而不應觸發 裝置偵測到低於 DEBUG 的程度時記錄 無效 (之後應盡可能減少記錄)。
  • 用於 String 物件時,會以隱含形式使用 + 運算子 建立採用預設值的 StringBuilder 例項 緩衝區空間 (16 個半形字元),且可能採用其他暫時 String 如需儲存大量結構化物件 建議使用 Cloud Bigtable所以明確建立 StringBuilder 物件並不是 比依賴預設的 + 運算子的費用高,且可大幅 )。請注意,如果程式碼 Log.v() 會在發布子版本上編譯及執行。 包括建構字串,即使沒有讀取記錄。
  • 可供他人讀取,且是為了 提供的發布子版本應該較為繁複 並以淺顯易懂的方式呈現這包括所有記錄功能 最高等級為 DEBUG。
  • 如果可以,請一行記錄。 一行長度上限為 80 或 100 個半形字元 可接受。避免超過 130 或 160 個字元的長度 (包括標記長度)。
  • 如果記錄報告成功,請勿在更高等級使用 VERBOSE。
  • 如果您使用暫時性記錄功能來診斷難以解決的問題 重現,保留為 DEBUG 或 VERBOSE 層級,並 如果封鎖時允許停用該功能 也就是編譯時間
  • 留意記錄中的安全性外洩問題。避免記錄隱私 可能不準確或不適當請特別注意,請避免記錄受保護內容的相關資訊。 特別是當您 因為要事先瞭解到 不會用於私人資訊或受保護內容
  • 一律不使用 System.out.println() (或為以下用途使用 printf()) 原生程式碼)。System.out 和 System.err 可享 重新導向至 /dev/null,因此您的輸出陳述式沒有任何 可見效果不過,發生了 這些呼叫仍會執行
  • 記錄的黃金法則在於 其他記錄也可視需要將其他記錄推送到緩衝區中,就像其他記錄可能會一樣 而不是挑戰自我

Javatest 樣式規則

請遵循測試方法命名慣例,並使用底線分隔 具體測試是針對特定個案進行測試這個風格讓 可讓您更輕鬆地查看測試的案例例如:

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}