AOSP-Java-Codestil für Mitwirkende

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Die Codestile auf dieser Seite sind strenge Regeln für das Beitragen 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 erkennen an, dass nicht der gesamte vorhandene Code diesen Regeln folgt, aber wir erwarten, dass alle neuen Codes konform sind. Beispiele für Terminologie, die für ein integrativeres Ökosystem verwendet und vermieden werden sollte, finden Sie unter Codierung mit Respekt .

Sei konsequent

Eine der einfachsten Regeln ist KONSISTENZ. 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 das auch tun. Wenn die Codekommentare kleine Kästchen mit Sternen um sich herum haben, lassen Sie Ihre Kommentare auch kleine Kästchen mit Sternen um sich herum haben.

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

Java-Sprachregeln

Android folgt den standardmäßigen Java-Codierungskonventionen mit den unten beschriebenen zusätzlichen Regeln.

Ausnahmen nicht ignorieren

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

Tu das nicht. Während Sie vielleicht denken, dass Ihr Code niemals auf diese Fehlerbedingung stoßen wird oder dass es nicht wichtig ist, sie zu behandeln, werden durch das Ignorieren dieser Art von Ausnahme Minen in Ihrem Code erstellt, die eines Tages von jemand anderem ausgelöst werden können. Sie müssen jede Ausnahme in Ihrem Code grundsätzlich 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 eigentlich das Richtige ist, aber man muss zumindest darüber nachdenken. In Java kann man sich dem gruseligen Gefühl nicht entziehen. “ – James Gosling

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

  • Übergeben 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.");
        }
      }
    
  • Gehen Sie vorsichtig mit dem Fehler um und ersetzen Sie den catch {} -Block durch einen geeigneten Wert.
      /** 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, also tun Sie es nur, wenn Sie sich sicher sind, dass bei Auftreten dieses Fehlers ein Absturz das Richtige 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 sicher sind, dass das Ignorieren der Ausnahme angemessen ist, können Sie sie als letzten Ausweg ignorieren, aber Sie müssen auch einen guten Grund dafür 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!
  }

Tu das nicht. In fast allen Fällen ist es unangemessen, generische Exception oder Throwable (vorzugsweise nicht Throwable , da es Error -Ausnahmen enthält). Es ist gefährlich, weil es bedeutet, dass unerwartete Ausnahmen (einschließlich Laufzeitausnahmen wie ClassCastException ) in der Fehlerbehandlung auf App-Ebene abgefangen werden. Es verschleiert die Fehlerbehandlungseigenschaften Ihres Codes, dh wenn jemand dem aufgerufenen Code eine neue Art von Ausnahme hinzufü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 auf oberster 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 geben Sie Kommentare ein, die erklären, warum es in diesem Zusammenhang sicher ist.

Alternativen zum Abfangen generischer Ausnahmen:

  • Fangen Sie jede Ausnahme separat als Teil eines Multi-Catch-Blocks ab, zum Beispiel:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Gestalten Sie Ihren Code um, um eine differenziertere Fehlerbehandlung mit mehreren Try-Blöcken zu erhalten. Trennen Sie das IO vom Parsing und behandeln Sie Fehler jeweils separat.
  • Lösen Sie die Ausnahme erneut aus. Oft müssen Sie die Ausnahme auf dieser Ebene sowieso nicht abfangen, lassen Sie sie einfach von der Methode auslösen.

Denken Sie daran, dass Ausnahmen Ihr Freund sind! Wenn der Compiler sich beschwert, dass Sie keine Ausnahme abfangen, schauen Sie nicht finster drein. 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 der Garbage Collection unterzogen wird. Während Finalizer für die Bereinigung (insbesondere von externen Ressourcen) praktisch sein können, gibt es keine Garantie dafür, wann ein Finalizer aufgerufen wird (oder ü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 Protokollnachricht vom Finalizer zu drucken, solange nicht erwartet wird, dass die Protokolle überflutet werden.

Importe vollständig qualifizieren

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 ausdrückliche Ausnahme wird für Java-Standardbibliotheken ( java.util.* , java.io.* usw.) und Komponententestcode ( junit.framework.* ) gemacht.

Regeln für Java-Bibliotheken

Es gibt Konventionen für die Verwendung der Java-Bibliotheken und -Tools von Android. In einigen Fällen hat sich die Konvention auf wichtige 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 fortzusetzen. Verwenden Sie beim Erstellen neuer Komponenten jedoch niemals veraltete Bibliotheken.

Regeln im Java-Stil

Verwenden Sie Javadoc-Standardkommentare

Jede Datei sollte oben eine Copyright-Anweisung 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 2022 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 Ihr Javadoc nur "sets Foo" sagen würde. Wenn die Methode etwas Komplexeres tut (z. B. eine Einschränkung erzwingt oder einen wichtigen Nebeneffekt hat), müssen Sie sie dokumentieren. Wenn es nicht offensichtlich ist, was die Eigenschaft "Foo" bedeutet, sollten Sie es 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 für das Schreiben von Javadoc-Kommentaren, aber Sie sollten den Anweisungen unter How to Write Doc Comments for the Javadoc Tool folgen.

Schreiben Sie kurze Methoden

Wenn möglich, halten Sie die Methoden klein und fokussiert. Wir erkennen an, 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 Standardstellen

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

Beschränken Sie den Variablenbereich

Beschränken Sie den Geltungsbereich 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 genügend Informationen haben, um eine Variable sinnvoll zu initialisieren, verschieben Sie die Deklaration, bis Sie dies getan haben.

Die Ausnahme sind 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 eine 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());
}

Importauszüge bestellen

Die Reihenfolge der Importanweisungen ist:

  1. Android-Importe
  2. Importe von Drittanbietern ( 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)
  • Getrennt durch eine Leerzeile zwischen den einzelnen Hauptgruppen ( android , com , junit , net , org , java , javax )

Ursprünglich gab es keine Stilanforderungen für die Reihenfolge, was bedeutete, dass IDEs entweder immer die Reihenfolge änderten oder IDE-Entwickler die automatischen Importverwaltungsfunktionen deaktivieren und die Importe manuell verwalten mussten. Dies wurde als schlecht empfunden. Als nach dem Java-Stil gefragt wurde, variierten die bevorzugten Stile stark und es kam darauf an, dass Android einfach „eine Reihenfolge auswählen und konsistent sein“ musste. Also haben wir einen Stil gewählt, den Styleguide aktualisiert und die IDEs dazu gebracht, ihm zu gehorchen. Wir erwarten, dass Importe in allen Paketen diesem Muster ohne zusätzlichen Entwicklungsaufwand entsprechen, wenn IDE-Benutzer am Code arbeiten.

Wir haben diesen Stil so gewählt, dass:

  • Die Importe, die sich die Leute zuerst ansehen möchten, stehen tendenziell ganz oben ( android ).
  • Die Importe, die die Leute zumindest anschauen wollen, sind 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 auf die gleiche Weise wie reguläre Importe bestellt werden.

Verwenden Sie Leerzeichen für Einrückungen

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 Funktionsaufrufen und Zuweisungen.

Empfohlen

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

Nicht empfohlen

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

Befolgen Sie die Benennungskonventionen für Felder

  • 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 Standardstil für Klammern

Setzen Sie geschweifte Klammern in dieselbe Zeile wie den Code davor, nicht in ihre eigene Zeile:

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

Wir benötigen geschweifte Klammern um die Anweisungen für eine Bedingung. Ausnahme: Wenn der gesamte Konditional (die Bedingung und der Körper) auf eine Zeile passen, können Sie (müssen aber nicht) alles auf eine Zeile setzen. Akzeptabel ist zum Beispiel:

if (condition) {
    body();
}

und das ist akzeptabel:

if (condition) body();

aber das ist nicht akzeptabel:

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

Zeilenlänge begrenzen

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

  • Wenn eine Kommentarzeile einen Beispielbefehl oder eine wörtliche URL enthält, die länger als 100 Zeichen ist, kann diese Zeile länger als 100 Zeichen sein, um das Ausschneiden und Einfügen zu erleichtern.
  • Importzeilen können das Limit überschreiten, da Menschen sie selten sehen (dies vereinfacht auch das Schreiben von Werkzeugen).

Verwenden Sie standardmäßige Java-Anmerkungen

Anmerkungen sollten anderen Modifikatoren für dasselbe Sprachelement vorangestellt werden. 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 sie einzeln pro Zeile in alphabetischer Reihenfolge auf.

Android-Standardpraktiken für die drei vordefinierten Anmerkungen 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 ein @deprecated Javadoc-Tag haben und es sollte eine alternative Implementierung benennen. Denken Sie außerdem daran, dass eine @Deprecated Methode immer noch funktionieren soll . Wenn Sie alten Code mit einem @deprecated Javadoc-Tag sehen, fügen Sie die @Deprecated Anmerkung hinzu.
  • Verwenden Sie die Annotation @Override immer dann, wenn eine Methode die Deklaration oder Implementierung einer übergeordneten Klasse überschreibt. Wenn Sie beispielsweise das Javadoc-Tag @inheritdocs verwenden und von einer Klasse (nicht von einer Schnittstelle) ableiten, müssen Sie auch anmerken, dass die Methode die Methode der übergeordneten Klasse überschreibt.
  • Verwenden Sie die Annotation @SuppressWarnings nur unter Umständen, in denen es unmöglich ist, eine Warnung zu beseitigen. 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 Anmerkung erforderlich ist, muss ihr ein TODO Kommentar vorangestellt werden, das die Bedingung „unmöglich zu beseitigen“ erklärt. Dies identifiziert normalerweise eine anstößige Klasse, die eine umständliche Schnittstelle hat. 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 Anmerkung erforderlich ist, refaktorisieren Sie den Code, um die Softwareelemente zu isolieren, auf die die Anmerkung zutrifft.

Akronyme als Wörter behandeln

Behandeln Sie Akronyme und Abkürzungen beim Benennen von Variablen, Methoden und Klassen als Wörter, um Namen besser lesbar zu machen:

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

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 daher Akronyme immer als 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 ("Beheben bis November 2005") oder ein bestimmtes Ereignis ("Entfernen Sie diesen Code, nachdem alle Produktionsmischer das Protokoll V7 verstanden haben") angeben. ).

Protokollieren Sie sparsam

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

  • ERROR : Verwenden, wenn etwas Fatales passiert ist, das heißt, etwas wird für den Benutzer sichtbare Folgen haben und nicht wiederherstellbar sein, ohne einige Daten zu löschen, Apps zu deinstallieren, die Datenpartitionen zu löschen oder das gesamte Gerät neu zu flashen (oder schlimmer). Diese Ebene wird immer protokolliert. Probleme, die eine gewisse Protokollierung auf der ERROR -Ebene rechtfertigen, sind gute Kandidaten, um sie einem Statistik-Erfassungsserver zu melden.
  • WARNING : Verwenden, wenn etwas Ernstes und Unerwartetes passiert ist, d. h. etwas, das für den Benutzer sichtbare Folgen hat, aber wahrscheinlich ohne Datenverlust wiederherstellbar ist, indem eine explizite Aktion ausgeführt wird, die vom Warten oder Neustarten einer App bis hin zum erneuten Herunterladen reicht eine neue Version einer App oder einen Neustart des Geräts. Diese Ebene wird immer protokolliert. Probleme, die eine Protokollierung auf der Stufe WARNING rechtfertigen, können auch für die Meldung an einen Server zum Sammeln von Statistiken in Betracht gezogen werden.
  • INFORMATIVE : Verwenden Sie dies, um darauf hinzuweisen, dass etwas Interessantes passiert ist, dh wenn eine Situation erkannt wird, die wahrscheinlich weitreichende Auswirkungen haben wird, obwohl es sich nicht unbedingt um einen Fehler handelt. Eine solche Bedingung sollte nur von einem Modul protokolliert werden, das glaubt, dass es das maßgeblichste in dieser Domäne ist (um eine doppelte Protokollierung durch nicht autoritative Komponenten zu vermeiden). Diese Ebene wird immer protokolliert.
  • DEBUG : Verwenden Sie diese Option, um weiter zu notieren, was auf dem Gerät passiert, was für die Untersuchung und Fehlerbehebung von unerwartetem Verhalten relevant sein könnte. 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 sogar bei Release-Builds protokolliert und muss von einem if (LOCAL_LOG) oder if LOCAL_LOGD) -Block umgeben sein, wobei LOCAL_LOG[D] in Ihrer Klasse oder Unterkomponente definiert ist, sodass die Möglichkeit besteht, alle diese Protokollierungen zu deaktivieren . Daher darf in einem if (LOCAL_LOG) -Block keine aktive Logik vorhanden sein. Der gesamte Zeichenfolgenaufbau für das Protokoll muss auch innerhalb des if (LOCAL_LOG) -Blocks platziert werden. Refaktorieren Sie den Protokollierungsaufruf nicht in einen Methodenaufruf, wenn dies dazu führt, dass die Zeichenfolgenerstellung außerhalb des if (LOCAL_LOG) -Blocks 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) -Block (oder Äquivalent) umgeben sein, damit sie standardmäßig kompiliert werden kann. Alle String-Builds werden aus Release-Builds entfernt und müssen innerhalb des if (LOCAL_LOGV) -Blocks erscheinen.

Anmerkungen

  • Innerhalb eines bestimmten Moduls sollte, außer auf der VERBOSE Ebene, ein Fehler möglichst 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 etwas Protokollierung hinzufügen, wenn dies wesentlich dazu beiträgt, das Problem zu isolieren.
  • 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 nur, wenn die Protokollierung dies ermöglicht Informationen, die dem Anrufer sonst nicht zur Verfügung stehen. Insbesondere ist es nicht erforderlich, 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, die vom Framework ordnungsgemäß behandelt werden, sollten keine Protokollierung über dem DEBUG -Level auslösen. Die einzige Situation, die die Protokollierung auf der INFORMATIVE -Ebene oder höher auslösen sollte, ist, wenn ein Modul oder eine App einen Fehler auf seiner eigenen Ebene oder von einer niedrigeren Ebene erkennt.
  • Wenn eine Bedingung, die normalerweise eine Protokollierung rechtfertigen würde, wahrscheinlich viele Male auftritt, kann es eine gute Idee sein, einen Ratenbegrenzungsmechanismus zu implementieren, um zu verhindern, dass die Protokolle mit vielen doppelten Kopien derselben (oder sehr ähnlichen) Informationen überlaufen.
  • Verluste der Netzwerkkonnektivität gelten als häufig und werden vollständig erwartet und sollten nicht unentgeltlich protokolliert werden. Ein Verlust der Netzwerkkonnektivität, der Folgen innerhalb einer App hat, sollte auf DEBUG oder VERBOSE Ebene protokolliert werden (je nachdem, ob die Folgen schwerwiegend genug und unerwartet genug sind, um in einem Release-Build protokolliert zu werden).
  • Ein vollständiges Dateisystem in einem Dateisystem zu haben, das für oder im Auftrag von Apps von Drittanbietern zugänglich ist, sollte nicht auf einer höheren Ebene als INFORMATIVE protokolliert werden.
  • Ungültige Daten, die aus einer nicht vertrauenswürdigen Quelle stammen (einschließlich einer Datei auf einem gemeinsam genutzten Speicher oder Daten, die über eine Netzwerkverbindung kommen), gelten als erwartet und sollten keine Protokollierung auf einer höheren Ebene als DEBUG auslösen, wenn sie als ungültig erkannt werden (und sogar dann protokollieren sollte so begrenzt wie möglich sein).
  • Bei Verwendung für String Objekte erstellt der + -Operator implizit eine StringBuilder Instanz mit der Standardpuffergröße (16 Zeichen) und möglicherweise anderen temporären String Objekten. Das explizite Erstellen von StringBuilder Objekten ist also nicht teurer, als sich auf den Standardoperator + zu verlassen (und kann viel effizienter sein). Denken Sie daran, dass Code, der Log.v() aufruft, bei Release-Builds kompiliert und ausgeführt wird, einschließlich der Erstellung der Zeichenfolgen, selbst wenn die Protokolle nicht gelesen werden.
  • Jede Protokollierung, die von anderen Personen gelesen werden soll und in Release-Builds verfügbar sein soll, sollte knapp, aber nicht kryptisch, und verständlich sein. Dies umfasst alle Protokollierungen bis zum DEBUG -Level.
  • Melden Sie sich nach Möglichkeit weiterhin in einer einzigen Zeile an. Zeilenlängen bis zu 80 oder 100 Zeichen sind akzeptabel. 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, halten Sie es auf der DEBUG oder VERBOSE Ebene und schließen Sie es mit if-Blöcken ein, die es ermöglichen, es zur Kompilierzeit zu deaktivieren.
  • Seien Sie vorsichtig bei Sicherheitslecks durch das Protokoll. Vermeiden Sie es, private Informationen zu protokollieren. Vermeiden Sie insbesondere das Protokollieren von Informationen über geschützte Inhalte. Dies ist besonders wichtig beim Schreiben von Rahmencode, da es nicht einfach ist, im Voraus zu wissen, was private Informationen oder geschützte Inhalte sind und was nicht.
  • Verwenden Sie niemals System.out.println() (oder printf() für nativen Code). System.out und System.err werden nach /dev/null umgeleitet, sodass Ihre Druckanweisungen keine sichtbaren Auswirkungen haben. Die gesamte Zeichenfolgenbildung, die für diese Aufrufe erfolgt, wird jedoch weiterhin ausgeführt.
  • Die goldene Regel der Protokollierung lautet, dass Ihre Protokolle nicht unnötigerweise andere Protokolle aus dem Puffer schieben dürfen, genauso wie andere Ihre Protokolle möglicherweise nicht verdrängen.

Stilregeln für Javatests

Befolgen Sie die Benennungskonventionen für Testmethoden und verwenden Sie einen Unterstrich, um zu trennen, was getestet wird, und von dem spezifischen Fall, der getestet wird. Dieser Stil macht es einfacher zu sehen, 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))
}