貢獻者適用的 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!
  }

請不要這麼做。大多數情況下,找出一般性 ExceptionThrowable (最好不是 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 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. 從第三方匯入 (comjunitnetorg)
  3. java」和「javax

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

  • 每個群組內的字母順序,中間是大寫字母 大寫字母 (例如,在 a 前面)
  • 每個主要群組之間以空白行分隔 (androidcomjunitnetorgjavajavax)

最初,順序並沒有樣式規定,意味著 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 以上等級是指模組或應用程式偵測到 獨立層級或來自較低層級的錯誤
  • 當一個常用於合理化記錄功能的條件時 建議您執行 頻率限制機制,以避免含有許多 重複相同 (或非常類似) 資訊的副本
  • 網路連線中斷的情形相當常見,而且完全沒有網路連線。 請勿故意記錄。網路斷線 會影響應用程式內後果的連線 DEBUGVERBOSE 層級 (取決於 結果嚴重到足以讓使用者留存在版本上 建構)。
  • 在可存取或應用程式的檔案系統中擁有完整的檔案系統 代表第三方應用程式不應在層級記錄 大於 INFORMATIVE 的
  • 來自任何不受信任的來源 (包括 共用儲存空間或透過網路 安全連線) 視為預期而不應觸發 裝置偵測到低於 DEBUG 的程度時記錄 無效 (之後應盡可能減少記錄)。
  • 用於 String 物件時,會以隱含形式使用 + 運算子 建立採用預設值的 StringBuilder 例項 緩衝區空間 (16 個半形字元),且可能採用其他暫時 String 如需儲存大量結構化物件 建議使用 Cloud Bigtable所以明確建立 StringBuilder 物件並不是 比依賴預設的 + 運算子的費用高,且可大幅 )。請注意,如果程式碼 Log.v() 會在發布子版本上編譯及執行。 包括建構字串,即使沒有讀取記錄。
  • 可供他人讀取,且是為了 提供的發布子版本應該較為繁複 並以淺顯易懂的方式呈現這包括所有記錄功能 最高等級為 DEBUG
  • 如果可以,請一行記錄。 一行長度上限為 80 或 100 個半形字元 可接受。避免超過 130 或 160 個字元的長度 (包括標記長度)。
  • 如果記錄報告成功,請勿在更高等級使用 VERBOSE
  • 如果您使用暫時性記錄功能來診斷難以解決的問題 重現,保留為 DEBUGVERBOSE 層級,並 如果封鎖時允許停用該功能 也就是編譯時間
  • 留意記錄中的安全性外洩問題。避免記錄隱私 可能不準確或不適當請特別注意,請避免記錄受保護內容的相關資訊。 特別是當您 因為要事先瞭解到 不會用於私人資訊或受保護內容
  • 一律不使用 System.out.println() (或為以下用途使用 printf()) 原生程式碼)。System.outSystem.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))
}