AOSP-Java-Codestil für Mitwirkende

Die Codestile auf dieser Seite sind strenge Regeln für die Bereitstellung von Java-Code zum Android Open Source Project (AOSP). Beiträge zur Android-Plattform, die diese Regeln nicht einhalten , werden im Allgemeinen nicht akzeptiert . Wir sind uns bewusst, dass nicht der gesamte vorhandene Code diesen Regeln folgt, wir erwarten jedoch, dass jeder neue Code konform ist. Beispiele für zu verwendende und zu vermeidende Terminologie für ein integrativeres Ökosystem finden Sie unter Codieren mit Respekt .

Seien Sie konsequent

Eine der einfachsten Regeln lautet: SEIEN SIE KONSEQUENT. Wenn Sie Code bearbeiten, nehmen Sie sich ein paar Minuten Zeit, um sich den umgebenden Code anzusehen und seinen Stil zu bestimmen. Wenn dieser Code Leerzeichen um die if Klauseln verwendet, sollten Sie dies auch tun. Wenn die Codekommentare von kleinen Kästchen mit Sternen umgeben sind, sorgen Sie dafür, dass auch Ihre Kommentare von kleinen Kästchen mit Sternen umgeben sind.

Der Zweck von Stilrichtlinien besteht darin, ein gemeinsames Codierungsvokabular zu haben, damit sich die Leser auf das konzentrieren können, was Sie sagen, und nicht darauf, wie Sie es sagen. Wir stellen hier globale Stilregeln vor, damit Sie den Wortschatz kennen, aber auch der lokale Stil ist wichtig. Wenn der Code, den Sie einer Datei hinzufügen, sich drastisch von dem vorhandenen Code um ihn herum unterscheidet, bringt er die Leser beim Lesen aus dem Rhythmus. Versuchen Sie dies zu vermeiden.

Java-Sprachregeln

Android folgt den Standard-Java-Codierungskonventionen mit den unten beschriebenen zusätzlichen Regeln.

Ignorieren Sie Ausnahmen nicht

Es kann verlockend sein, Code zu schreiben, der eine Ausnahme ignoriert, wie zum Beispiel:

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

Tun Sie das nicht. Während Sie vielleicht denken, dass Ihr Code niemals auf diesen Fehlerzustand stoßen wird oder dass es nicht wichtig ist, damit umzugehen, führt das Ignorieren dieser Art von Ausnahme zu Minen in Ihrem Code, die eines Tages von jemand anderem ausgelöst werden können. Sie müssen jede Ausnahme in Ihrem Code prinzipiell behandeln. Die konkrete Handhabung variiert je nach Fall.

„Jedes Mal, wenn jemand eine leere Catch-Klausel hat, sollte er ein gruseliges Gefühl haben. Es gibt definitiv Zeiten, in denen es tatsächlich das Richtige ist, aber man muss zumindest darüber nachdenken. In Java kann man dem gruseligen Gefühl nicht entkommen. " — James Gosling

Akzeptable Alternativen (in der Reihenfolge ihrer Präferenz) sind:

  • Werfen Sie die Ausnahme an den Aufrufer Ihrer Methode.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Lösen Sie eine neue Ausnahme aus, die Ihrer Abstraktionsebene entspricht.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Behandeln Sie den Fehler ordnungsgemäß und ersetzen Sie ihn durch einen geeigneten Wert im catch {} -Block.
      /** 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
        }
      }
    
  • Fangen Sie die Ausnahme ab und lösen Sie eine neue Instanz von RuntimeException aus. Dies ist gefährlich. Tun Sie dies daher nur, wenn Sie sicher sind, dass bei Auftreten dieses Fehlers ein Absturz die richtige Maßnahme ist.
      /** 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);
        }
      }
    
  • Wenn Sie davon überzeugt sind, dass das Ignorieren der Ausnahme angemessen ist, können Sie sie als letzten Ausweg ignorieren. Sie müssen jedoch auch den Grund dafür mit einer guten Begründung angeben.
    /** 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.
        }
    }
    

Fangen Sie keine generischen Ausnahmen ab

Es kann verlockend sein, beim Abfangen von Ausnahmen faul zu sein und so etwas zu tun:

  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!
  }

Tun Sie das nicht. In fast allen Fällen ist es ungeeignet, eine generische Exception oder Throwable abzufangen (vorzugsweise nicht Throwable , da es Error enthält). Dies ist gefährlich, da dadurch Ausnahmen, die Sie nie erwartet hätten (einschließlich Laufzeitausnahmen wie ClassCastException ), bei der Fehlerbehandlung auf App-Ebene erfasst werden. Es verschleiert die Fehlerbehandlungseigenschaften Ihres Codes. Wenn also jemand einen neuen Ausnahmetyp in den von Ihnen aufgerufenen Code einfügt, weist der Compiler nicht darauf hin, dass Sie den Fehler anders behandeln müssen. In den meisten Fällen sollten Sie verschiedene Arten von Ausnahmen nicht auf die gleiche Weise behandeln.

Die seltene Ausnahme von dieser Regel ist Testcode und Code der obersten Ebene, bei dem Sie alle Arten von Fehlern abfangen möchten (um zu verhindern, dass sie in einer Benutzeroberfläche angezeigt werden, oder um einen Batch-Job am Laufen zu halten). In diesen Fällen können Sie eine generische Exception (oder Throwable ) abfangen und den Fehler entsprechend behandeln. Denken Sie jedoch sorgfältig darüber nach, bevor Sie dies tun, und erläutern Sie in Kommentaren, warum dies in diesem Zusammenhang sicher ist.

Alternativen zum Abfangen allgemeiner Ausnahmen:

  • Fangen Sie jede Ausnahme separat als Teil eines Multi-Catch-Blocks ab, zum Beispiel:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refaktorieren Sie Ihren Code, um eine detailliertere Fehlerbehandlung mit mehreren Try-Blöcken zu ermöglichen. Teilen Sie die E/A vom Parsen auf und behandeln Sie Fehler jeweils separat.
  • Die Ausnahme erneut auslösen. Oftmals müssen Sie die Ausnahme auf dieser Ebene ohnehin nicht abfangen, sondern lassen Sie sie einfach von der Methode auslösen.

Denken Sie daran, dass Ausnahmen Ihr Freund sind! Wenn der Compiler sich darüber beschwert, dass Sie keine Ausnahme abfangen, machen Sie kein böses Gesicht. Lächeln! Der Compiler hat es Ihnen lediglich erleichtert, Laufzeitprobleme in Ihrem Code zu erkennen.

Verwenden Sie keine Finalizer

Finalizer sind eine Möglichkeit, einen Teil des Codes auszuführen, wenn ein Objekt durch Garbage Collection erfasst wird. Während Finalizer bei der Bereinigung nützlich sein können (insbesondere bei externen Ressourcen), gibt es keine Garantie dafür, wann ein Finalizer aufgerufen wird (oder sogar, dass er überhaupt aufgerufen wird).

Android verwendet keine Finalizer. In den meisten Fällen können Sie stattdessen eine gute Ausnahmebehandlung verwenden. Wenn Sie unbedingt einen Finalizer benötigen, definieren Sie eine close() Methode (oder ähnliches) und dokumentieren Sie genau, wann diese Methode aufgerufen werden muss (siehe InputStream für ein Beispiel). In diesem Fall ist es angemessen, aber nicht erforderlich, eine kurze Protokollmeldung vom Finalizer auszudrucken, solange nicht damit gerechnet wird, dass die Protokolle überflutet werden.

Vollständig qualifizierte Importe

Wenn Sie die Klasse Bar aus dem Paket foo verwenden möchten, gibt es zwei Möglichkeiten, sie zu importieren:

  • import foo.*;

    Reduziert möglicherweise die Anzahl der Importanweisungen.

  • import foo.Bar;

    Macht deutlich, welche Klassen verwendet werden, und der Code ist für Betreuer besser lesbar.

Verwenden Sie import foo.Bar; zum Importieren des gesamten Android-Codes. Eine explizite Ausnahme gilt für Java-Standardbibliotheken ( java.util.* , java.io.* usw.) und Unit-Test-Code ( junit.framework.* ).

Regeln der Java-Bibliothek

Es gibt Konventionen für die Verwendung der Java-Bibliotheken und -Tools von Android. In einigen Fällen hat sich die Konvention in wichtiger Weise geändert und älterer Code verwendet möglicherweise ein veraltetes Muster oder eine veraltete Bibliothek. Wenn Sie mit solchem ​​Code arbeiten, ist es in Ordnung, den bestehenden Stil beizubehalten. Verwenden Sie beim Erstellen neuer Komponenten jedoch niemals veraltete Bibliotheken.

Regeln im Java-Stil

Verwenden Sie Javadoc-Standardkommentare

Jede Datei sollte oben einen Copyright-Hinweis haben, gefolgt von Paket- und Importanweisungen (jeder Block durch eine Leerzeile getrennt) und schließlich die Klassen- oder Schnittstellendeklaration. Beschreiben Sie in den Javadoc-Kommentaren, was die Klasse oder Schnittstelle tut.

/*
 * Copyright 2024 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 {
    ...
}

Jede Klasse und nicht-triviale öffentliche Methode, die Sie schreiben, muss einen Javadoc-Kommentar mit mindestens einem Satz enthalten, der beschreibt, was die Klasse oder Methode tut. Dieser Satz sollte mit einem beschreibenden Verb der dritten Person beginnen.

Beispiele

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

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

oder

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

Sie müssen kein Javadoc für triviale Get- und Set-Methoden wie setFoo() schreiben, wenn in Ihrem Javadoc nur „sets Foo“ steht. Wenn die Methode etwas Komplexeres bewirkt (z. B. die Durchsetzung einer Einschränkung oder einen wichtigen Nebeneffekt hat), müssen Sie dies dokumentieren. Wenn nicht klar ist, was die Eigenschaft „Foo“ bedeutet, sollten Sie dies dokumentieren.

Jede Methode, die Sie schreiben, ob öffentlich oder nicht, würde von Javadoc profitieren. Öffentliche Methoden sind Teil einer API und erfordern daher Javadoc. Android erzwingt keinen bestimmten Stil zum Schreiben von Javadoc-Kommentaren, Sie sollten jedoch den Anweisungen unter So schreiben Sie Doc-Kommentare für das Javadoc-Tool folgen.

Schreiben Sie kurze Methoden

Wenn möglich, sollten Sie die Methoden klein und fokussiert halten. Wir wissen, dass lange Methoden manchmal angemessen sind, daher gibt es keine feste Begrenzung der Methodenlänge. Wenn eine Methode etwa 40 Zeilen überschreitet, überlegen Sie, ob sie aufgeteilt werden kann, ohne die Struktur des Programms zu beeinträchtigen.

Definieren Sie Felder an Standardorten

Definieren Sie Felder entweder am Anfang der Datei oder unmittelbar vor den Methoden, die sie verwenden.

Begrenzen Sie den Variablenbereich

Beschränken Sie den Umfang lokaler Variablen auf ein Minimum. Dies erhöht die Lesbarkeit und Wartbarkeit Ihres Codes und verringert die Fehlerwahrscheinlichkeit. Deklarieren Sie jede Variable im innersten Block, der alle Verwendungen der Variablen einschließt.

Deklarieren Sie lokale Variablen an der Stelle, an der sie zum ersten Mal verwendet werden. Nahezu jede lokale Variablendeklaration sollte einen Initialisierer enthalten. Wenn Sie noch nicht über genügend Informationen verfügen, um eine Variable sinnvoll zu initialisieren, verschieben Sie die Deklaration bis dahin.

Die Ausnahme bilden Try-Catch-Anweisungen. Wenn eine Variable mit dem Rückgabewert einer Methode initialisiert wird, die eine geprüfte Ausnahme auslöst, muss sie innerhalb eines Try-Blocks initialisiert werden. Wenn der Wert außerhalb des Try-Blocks verwendet werden muss, muss er vor dem Try-Block deklariert werden, wo er noch nicht sinnvoll initialisiert werden kann:

// 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));

Sie können diesen Fall jedoch sogar vermeiden, indem Sie den Try-Catch-Block in einer Methode kapseln:

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

Deklarieren Sie Schleifenvariablen in der for-Anweisung selbst, es sei denn, es gibt einen zwingenden Grund, etwas anderes zu tun:

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

Und

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

Importanweisungen bestellen

Die Reihenfolge der Importanweisungen ist:

  1. Android-Importe
  2. Importe von Dritten ( com , junit , net , org )
  3. java und javax

Um genau mit den IDE-Einstellungen übereinzustimmen, sollten die Importe wie folgt aussehen:

  • Alphabetisch innerhalb jeder Gruppierung, mit Großbuchstaben vor Kleinbuchstaben (z. B. Z vor a)
  • Durch eine Leerzeile zwischen den einzelnen Hauptgruppen getrennt ( android , com , junit , net , org , java , javax )

Ursprünglich gab es keine Stilvorgaben für die Reihenfolge, was bedeutete, dass IDEs entweder ständig die Reihenfolge änderten oder IDE-Entwickler die automatischen Importverwaltungsfunktionen deaktivieren und die Importe manuell verwalten mussten. Dies wurde als schlecht eingestuft. Bei der Frage nach dem Java-Stil waren die bevorzugten Stile sehr unterschiedlich und es kam darauf an, dass Android einfach „eine Reihenfolge auswählen und konsistent sein“ musste. Also haben wir einen Stil ausgewählt, den Styleguide aktualisiert und dafür gesorgt, dass die IDEs diesem folgen. Wir gehen davon aus, dass bei der Arbeit von IDE-Benutzern am Code die Importe in allen Paketen diesem Muster entsprechen, ohne dass zusätzlicher technischer Aufwand erforderlich ist.

Wir haben diesen Stil so gewählt, dass:

  • Die Importe, die sich die Leute zuerst ansehen möchten, befinden sich in der Regel ganz oben ( android ).
  • Die Importe, die man sich zumindest ansehen möchte, befinden sich tendenziell ganz unten ( java ).
  • Menschen können dem Stil leicht folgen.
  • IDEs können dem Stil folgen.

Stellen Sie statische Importe über alle anderen Importe, die genauso geordnet sind wie reguläre Importe.

Verwenden Sie Leerzeichen zum Einrücken

Wir verwenden vier (4) Leerzeicheneinzüge für Blöcke und niemals Tabulatoren. Halten Sie sich im Zweifelsfall an den umgebenden Code.

Wir verwenden acht (8) Leerzeicheneinzüge für Zeilenumbrüche, einschließlich Funktionsaufrufe und Zuweisungen.

Empfohlen

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

Nicht empfohlen

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

Befolgen Sie die Konventionen für die Feldbenennung

  • Nicht öffentliche, nicht statische Feldnamen beginnen mit m .
  • Statische Feldnamen beginnen mit s .
  • Andere Felder beginnen mit einem Kleinbuchstaben.
  • Statische Endfelder (Konstanten, zutiefst unveränderlich) sind ALL_CAPS_WITH_UNDERSCORES .

Zum Beispiel:

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

Verwenden Sie den Standard-Klammerstil

Setzen Sie geschweifte Klammern in dieselbe Zeile wie der Code davor, nicht in eine eigene Zeile:

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

Für eine Bedingung benötigen wir geschweifte Klammern um die Anweisungen. Ausnahme: Wenn die gesamte Bedingung (die Bedingung und der Körper) in eine Zeile passt, können Sie (sind aber nicht dazu verpflichtet) alles in eine Zeile schreiben. Dies ist beispielsweise akzeptabel:

if (condition) {
    body();
}

und das ist akzeptabel:

if (condition) body();

aber das ist nicht akzeptabel:

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

Leitungslänge begrenzen

Jede Textzeile in Ihrem Code sollte höchstens 100 Zeichen lang sein. Obwohl diese Regel viel diskutiert wurde, bleibt die Entscheidung bestehen, dass 100 Zeichen das Maximum sind, mit den folgenden Ausnahmen :

  • Wenn eine Kommentarzeile einen Beispielbefehl oder eine wörtliche URL mit mehr als 100 Zeichen enthält, kann diese Zeile zum einfacheren Ausschneiden und Einfügen länger als 100 Zeichen sein.
  • Importzeilen können den Grenzwert überschreiten, weil Menschen sie selten sehen (dies vereinfacht auch das Schreiben von Werkzeugen).

Verwenden Sie Standard-Java-Anmerkungen

Anmerkungen sollten vor anderen Modifikatoren für dasselbe Sprachelement stehen. Einfache Markierungsanmerkungen (z. B. @Override ) können in derselben Zeile wie das Sprachelement aufgeführt werden. Wenn mehrere Anmerkungen oder parametrisierte Anmerkungen vorhanden sind, listen Sie diese einzeln pro Zeile in alphabetischer Reihenfolge auf.

Android-Standardpraktiken für die drei vordefinierten Annotationen in Java sind:

  • Verwenden Sie die Annotation @Deprecated immer dann, wenn von der Verwendung des annotierten Elements abgeraten wird. Wenn Sie die Annotation @Deprecated verwenden, müssen Sie auch über ein Javadoc-Tag @deprecated verfügen und es sollte eine alternative Implementierung benennen. Denken Sie außerdem daran, dass eine @Deprecated Methode weiterhin funktionieren sollte . Wenn Sie alten Code mit dem Javadoc-Tag @deprecated sehen, fügen Sie die Annotation @Deprecated hinzu.
  • Verwenden Sie die Annotation @Override immer dann, wenn eine Methode die Deklaration oder Implementierung einer Superklasse überschreibt. Wenn Sie beispielsweise das Javadoc-Tag @inheritdocs verwenden und von einer Klasse (nicht von einer Schnittstelle) ableiten, müssen Sie auch annotieren, dass die Methode die Methode der übergeordneten Klasse überschreibt.
  • Verwenden Sie die Annotation @SuppressWarnings nur unter Umständen, unter denen es unmöglich ist, eine Warnung zu entfernen. Wenn eine Warnung diesen „Unmöglich zu beseitigen“-Test besteht, muss die Annotation @SuppressWarnings verwendet werden, um sicherzustellen, dass alle Warnungen tatsächliche Probleme im Code widerspiegeln.

    Wenn eine @SuppressWarnings Annotation erforderlich ist, muss ihr ein TODO Kommentar vorangestellt werden, der die Bedingung „Unmöglich zu beseitigen“ erklärt. Dies identifiziert normalerweise eine problematische Klasse, die über eine umständliche Schnittstelle verfügt. Zum Beispiel:

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

    Wenn eine @SuppressWarnings Annotation erforderlich ist, überarbeiten Sie den Code, um die Softwareelemente zu isolieren, auf die die Annotation angewendet wird.

Behandeln Sie Akronyme wie Wörter

Behandeln Sie Akronyme und Abkürzungen bei der Benennung von Variablen, Methoden und Klassen als Wörter, um die Lesbarkeit der Namen zu verbessern:

Gut Schlecht
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
Klasse HTML Klasse HTML
String-URL String-URL
lange ID langer Ausweis

Da sowohl die JDK- als auch die Android-Codebasis in Bezug auf Akronyme inkonsistent sind, ist es praktisch unmöglich, mit dem umgebenden Code konsistent zu sein. Behandeln Sie Akronyme daher immer wie Wörter.

Verwenden Sie TODO-Kommentare

Verwenden Sie TODO Kommentare für Code, der vorübergehend ist, eine kurzfristige Lösung darstellt oder gut genug, aber nicht perfekt ist. Diese Kommentare sollten die Zeichenfolge TODO in Großbuchstaben enthalten, gefolgt von einem Doppelpunkt:

// TODO: Remove this code after the UrlTable2 has been checked in.

Und

// TODO: Change this to use a flag instead of a constant.

Wenn Ihr TODO die Form „Zu einem späteren Zeitpunkt etwas tun“ hat, stellen Sie sicher, dass Sie entweder ein bestimmtes Datum („Behebung bis November 2005“) oder ein bestimmtes Ereignis („Entfernen Sie diesen Code, nachdem alle Produktionsmischer Protokoll V7 verstehen“) angeben. ).

Protokollieren Sie sparsam

Die Protokollierung ist zwar notwendig, wirkt sich jedoch negativ auf die Leistung aus und verliert ihren Nutzen, wenn sie nicht einigermaßen knapp gehalten wird. Die Protokollierungsfunktionen bieten fünf verschiedene Protokollierungsebenen:

  • ERROR : Wird verwendet, wenn etwas Schwerwiegendes passiert ist, das heißt, etwas hat für den Benutzer sichtbare Folgen und kann nicht wiederhergestellt werden, ohne dass einige Daten gelöscht, Apps deinstalliert, die Datenpartitionen gelöscht oder das gesamte Gerät neu geflasht werden (oder Schlimmeres). Dieser Level wird immer protokolliert. Probleme, die eine Protokollierung auf der Ebene ERROR rechtfertigen, eignen sich gut für die Meldung an einen Statistikserver.
  • WARNING : Verwenden Sie diese Option, wenn etwas Schwerwiegendes und Unerwartetes passiert ist, d. h. etwas, das für den Benutzer sichtbare Folgen hat, aber wahrscheinlich ohne Datenverlust durch Ausführen einer expliziten Aktion wiederhergestellt werden kann, angefangen vom Warten oder Neustarten einer App bis hin zum erneuten Herunterladen eine neue Version einer App oder ein Neustart des Geräts. Dieser Level wird immer protokolliert. Probleme, die eine Protokollierung auf der WARNING Ebene rechtfertigen, können auch für die Meldung an einen Server zur Statistikerfassung in Betracht gezogen werden.
  • INFORMATIVE : Wird verwendet, um darauf hinzuweisen, dass etwas Interessantes passiert ist, d. h. wenn eine Situation erkannt wird, die wahrscheinlich weitreichende Auswirkungen haben wird, bei der es sich jedoch nicht unbedingt um einen Fehler handelt. Eine solche Bedingung sollte nur von einem Modul protokolliert werden, das davon ausgeht, dass es in dieser Domäne das maßgeblichste ist (um eine doppelte Protokollierung durch nicht autorisierende Komponenten zu vermeiden). Dieser Level wird immer protokolliert.
  • DEBUG : Verwenden Sie diese Option, um weitere Ereignisse auf dem Gerät zu notieren, die für die Untersuchung und Fehlerbehebung unerwarteter Verhaltensweisen relevant sein könnten. Protokollieren Sie nur das, was erforderlich ist, um genügend Informationen darüber zu sammeln, was mit Ihrer Komponente vor sich geht. Wenn Ihre Debug-Protokolle das Protokoll dominieren, sollten Sie die ausführliche Protokollierung verwenden.

    Diese Ebene wird auch bei Release-Builds protokolliert und muss von einem if (LOCAL_LOG) oder if LOCAL_LOGD) umgeben sein, wobei LOCAL_LOG[D] in Ihrer Klasse oder Unterkomponente definiert ist, sodass die Möglichkeit besteht, die gesamte Protokollierung zu deaktivieren . Daher darf in einem if (LOCAL_LOG) keine aktive Logik vorhanden sein. Der gesamte String-Aufbau für das Protokoll muss auch im if (LOCAL_LOG) platziert werden. Refaktorieren Sie den Protokollierungsaufruf nicht in einen Methodenaufruf, wenn dies dazu führt, dass die Zeichenfolgenerstellung außerhalb des if (LOCAL_LOG) stattfindet.

    Es gibt einen Code, der immer noch sagt if (localLOGV) . Dies wird ebenfalls als akzeptabel angesehen, obwohl der Name nicht dem Standard entspricht.

  • VERBOSE : Für alles andere verwenden. Diese Ebene wird nur bei Debug-Builds protokolliert und sollte von einem if (LOCAL_LOGV) (oder einem Äquivalent) umgeben sein, damit sie standardmäßig herauskompiliert werden kann. Jegliche String-Erstellung wird aus Release-Builds entfernt und muss im if (LOCAL_LOGV) erscheinen.

Anmerkungen

  • Innerhalb eines bestimmten Moduls, außer auf der VERBOSE Ebene, sollte ein Fehler nach Möglichkeit nur einmal gemeldet werden. Innerhalb einer einzelnen Kette von Funktionsaufrufen innerhalb eines Moduls sollte nur die innerste Funktion den Fehler zurückgeben, und Aufrufer im selben Modul sollten nur dann eine Protokollierung hinzufügen, wenn dies erheblich zur Eingrenzung des Problems beiträgt.
  • Wenn in einer Kette von Modulen, außer auf der VERBOSE Ebene, ein Modul auf niedrigerer Ebene ungültige Daten erkennt, die von einem Modul auf höherer Ebene stammen, sollte das Modul auf niedrigerer Ebene diese Situation nur im DEBUG Protokoll protokollieren, und zwar nur, wenn die Protokollierung dies zulässt Informationen, die dem Anrufer sonst nicht zur Verfügung stehen. Insbesondere besteht keine Notwendigkeit, Situationen zu protokollieren, in denen eine Ausnahme ausgelöst wird (die Ausnahme sollte alle relevanten Informationen enthalten) oder in denen die einzigen protokollierten Informationen in einem Fehlercode enthalten sind. Dies ist besonders wichtig bei der Interaktion zwischen dem Framework und Apps, und Bedingungen, die durch Apps von Drittanbietern verursacht werden und vom Framework ordnungsgemäß verarbeitet werden, sollten keine Protokollierung über der DEBUG Ebene auslösen. Die einzigen Situationen, die eine Protokollierung auf der Ebene INFORMATIVE oder höher auslösen sollten, sind, wenn ein Modul oder eine App einen Fehler auf ihrer eigenen Ebene oder von einer niedrigeren Ebene erkennt.
  • Wenn ein Zustand, der normalerweise eine gewisse Protokollierung rechtfertigen würde, wahrscheinlich häufig auftritt, kann es eine gute Idee sein, einen Mechanismus zur Ratenbegrenzung zu implementieren, um zu verhindern, dass die Protokolle mit vielen Duplikaten derselben (oder sehr ähnlichen) Informationen überlaufen.
  • Verluste der Netzwerkkonnektivität gelten als häufig und sind durchaus zu erwarten und sollten nicht ohne Grund protokolliert werden. Ein Verlust der Netzwerkkonnektivität, der Konsequenzen innerhalb einer App hat, sollte auf der DEBUG oder VERBOSE Ebene protokolliert werden (je nachdem, ob die Konsequenzen schwerwiegend genug und unerwartet genug sind, um in einem Release-Build protokolliert zu werden).
  • Das Vorhandensein eines vollständigen Dateisystems in einem Dateisystem, auf das Apps von Drittanbietern zugreifen können oder in deren Auftrag sie ausgeführt werden, sollte nicht auf einer höheren Ebene als „INFORMATIVE“ protokolliert werden.
  • Ungültige Daten, die von einer nicht vertrauenswürdigen Quelle stammen (einschließlich Dateien im freigegebenen Speicher oder Daten, die über eine Netzwerkverbindung eingehen), gelten als erwartet und sollten keine Protokollierung auf einer höheren Ebene als DEBUG auslösen, wenn sie als ungültig erkannt werden (und selbst dann keine Protokollierung). sollte möglichst begrenzt sein).
  • Bei Verwendung für String Objekte erstellt der Operator „ + implizit eine StringBuilder Instanz mit der Standardpuffergröße (16 Zeichen) und möglicherweise andere temporäre String Objekte. Das explizite Erstellen StringBuilder Objekten ist also nicht teurer als die Verwendung des Standardoperators + (und kann viel effizienter sein). Beachten Sie, dass Code, der Log.v() aufruft, bei Release-Builds kompiliert und ausgeführt wird, einschließlich der Erstellung der Zeichenfolgen, auch wenn die Protokolle nicht gelesen werden.
  • Jede Protokollierung, die dazu gedacht ist, von anderen Leuten gelesen zu werden und in Release-Builds verfügbar zu sein, sollte knapp, aber nicht kryptisch sein, und verständlich sein. Dies umfasst die gesamte Protokollierung bis zur DEBUG Ebene.
  • Protokollieren Sie nach Möglichkeit weiterhin in einer einzelnen Zeile. Zeilenlängen bis zu 80 oder 100 Zeichen sind zulässig. Vermeiden Sie nach Möglichkeit Längen von mehr als etwa 130 oder 160 Zeichen (einschließlich der Länge des Tags).
  • Wenn die Protokollierung Erfolge meldet, verwenden Sie sie niemals auf höheren Ebenen als VERBOSE .
  • Wenn Sie die temporäre Protokollierung verwenden, um ein Problem zu diagnostizieren, das schwer zu reproduzieren ist, belassen Sie es auf der DEBUG oder VERBOSE -Ebene und schließen Sie es in if-Blöcke ein, die eine Deaktivierung zur Kompilierungszeit ermöglichen.
  • Seien Sie vorsichtig bei Sicherheitslecks im Protokoll. Vermeiden Sie die Protokollierung privater Informationen. Vermeiden Sie insbesondere die Protokollierung von Informationen über geschützte Inhalte. Dies ist besonders wichtig, wenn Sie Framework-Code schreiben, da es nicht einfach ist, im Voraus zu wissen, was private Informationen oder geschützte Inhalte sein werden und was nicht.
  • Verwenden Sie niemals System.out.println() (oder printf() für nativen Code). System.out und System.err werden zu /dev/null umgeleitet, sodass Ihre Druckanweisungen keine sichtbaren Auswirkungen haben. Der gesamte Zeichenfolgenaufbau, der für diese Aufrufe erfolgt, wird jedoch weiterhin ausgeführt.
  • Die goldene Regel bei der Protokollierung lautet, dass Ihre Protokolle andere Protokolle nicht unnötig aus dem Puffer verdrängen dürfen, genauso wie andere Ihre Protokolle möglicherweise nicht unnötigerweise aus dem Puffer verdrängen.

Stilregeln für Javatests

Befolgen Sie die Benennungskonventionen für Testmethoden und verwenden Sie einen Unterstrich, um das Getestete vom spezifischen Testfall zu trennen. Dieser Stil macht es einfacher zu erkennen, welche Fälle getestet werden. Zum Beispiel:

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