기여자를 위한 AOSP 자바 코드 스타일

이 페이지의 코드 스타일은 Android 오픈소스 프로젝트(AOSP)에 자바 코드를 제공하기 위한 엄격한 규칙입니다. 이러한 규칙을 준수하지 않는 Android 플랫폼에 대한 기여는 일반적으로 허용되지 않습니다 . 모든 기존 코드가 이러한 규칙을 따르는 것은 아니지만 모든 새 코드가 이를 준수할 것으로 기대합니다. 보다 포괄적인 생태계를 위해 사용하고 피해야 할 용어의 예는 코딩 관련 항목을 참조하십시오.

일관성 유지

가장 간단한 규칙 중 하나는 일관성을 유지하는 것입니다. 코드를 편집하는 경우 몇 분 동안 주변 코드를 살펴보고 스타일을 결정하십시오. 해당 코드에서 if 절 주위에 공백을 사용하는 경우에도 그렇게 해야 합니다. 코드 주석 주위에 작은 별 상자가 있는 경우 주석 주위에도 작은 별 상자가 표시되도록 하십시오.

스타일 가이드라인을 갖는 요점은 코딩의 공통 어휘를 갖는 것이므로 독자는 말하는 방식이 아니라 말하는 내용에 집중할 수 있습니다. 어휘를 알 수 있도록 여기에 전역 스타일 규칙을 제시하지만 지역 스타일도 중요합니다. 파일에 추가하는 코드가 주변의 기존 코드와 크게 다른 경우 독자가 읽을 때 리듬에서 벗어날 수 있습니다. 이것을 피하십시오.

자바 언어 규칙

Android는 아래에 설명된 추가 규칙과 함께 표준 Java 코딩 규칙을 따릅니다.

예외를 무시하지 마십시오

다음과 같이 예외를 무시하는 코드를 작성하고 싶을 수 있습니다.

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

이러지 마. 코드에서 이 오류 조건이 발생하지 않거나 이를 처리하는 것이 중요하지 않다고 생각할 수 있지만 이러한 유형의 예외를 무시하면 언젠가 다른 사람이 트리거할 수 있도록 코드에 지뢰가 생성됩니다. 원칙에 따라 코드의 모든 예외를 처리해야 합니다. 특정 처리는 경우에 따라 다릅니다.

" 누군가가 비어 있는 catch 절을 가질 때마다 소름끼치는 느낌이 들 것입니다. 그것이 실제로 올바른 일인 경우가 분명히 있지만 적어도 그것에 대해 생각해야 합니다. Java에서는 소름 끼치는 느낌을 피할 수 없습니다. " — 제임스 고슬링

허용되는 대안(선호 순서대로)은 다음과 같습니다.

  • 메서드 호출자에게 예외를 발생시킵니다.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • 추상화 수준에 적합한 새 예외를 throw합니다.
      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 을 catch하는 것은 부적절합니다( Error 예외가 포함되어 있으므로 Throwable 이 아닌 것이 좋습니다). 예상하지 못한 예외( ClassCastException 과 같은 런타임 예외 포함)가 앱 수준 오류 처리에서 포착된다는 것을 의미하기 때문에 위험합니다. 이는 코드의 오류 처리 속성을 모호하게 합니다. 즉, 누군가가 호출하는 코드에 새로운 유형의 예외를 추가하더라도 컴파일러는 오류를 다르게 처리해야 한다고 지적하지 않습니다. 대부분의 경우 다른 유형의 예외를 동일한 방식으로 처리하면 안 됩니다.

이 규칙의 드문 예외는 모든 종류의 오류를 포착하려는 테스트 코드 및 최상위 코드입니다(UI에 오류가 표시되는 것을 방지하거나 일괄 작업 실행을 유지하기 위해). 이러한 경우 일반 Exception (또는 Throwable )을 포착하고 오류를 적절하게 처리할 수 있습니다. 그러나이 작업을 수행하기 전에 신중하게 생각하고 이 컨텍스트에서 안전한 이유를 설명하는 주석을 작성하십시오.

일반 예외 포착에 대한 대안:

  • 멀티 캐치 블록의 일부로 각 예외를 개별적으로 캐치합니다(예:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
    .
  • 여러 try 블록을 사용하여 보다 세분화된 오류 처리를 위해 코드를 리팩터링합니다. 구문 분석에서 IO를 분할하고 각각의 경우에 별도로 오류를 처리합니다.
  • 예외를 다시 발생시킵니다. 어쨌든 이 수준에서 예외를 잡을 필요가 없는 경우가 많습니다. 메서드가 예외를 throw하도록 놔두세요.

예외는 당신의 친구라는 것을 기억하십시오! 컴파일러가 예외를 포착하지 못한다고 불평할 때 얼굴을 찌푸리지 마십시오. 웃다! 컴파일러를 사용하면 코드에서 런타임 문제를 더 쉽게 파악할 수 있습니다.

종료자를 사용하지 마십시오

종료자는 개체가 가비지 수집될 때 코드 청크를 실행하는 방법입니다. 종료자는 정리(특히 외부 리소스)에 편리할 수 있지만 종료자가 언제 호출될지(또는 전혀 호출되지 않을지)에 대한 보장은 없습니다.

Android는 종료자를 사용하지 않습니다. 대부분의 경우 좋은 예외 처리를 대신 사용할 수 있습니다. 종료자가 절대적으로 필요한 경우 close() 메서드(또는 유사 항목)를 정의하고 해당 메서드를 호출해야 하는 시점을 정확하게 문서화합니다(예는 InputStream 참조). 이 경우 로그가 넘칠 것으로 예상되지 않는 한 종료자에서 짧은 로그 메시지를 인쇄하는 것이 적절하지만 필수는 아닙니다.

정규화된 수입품

패키지 foo 에서 Bar 클래스를 사용하려는 경우 가져올 수 있는 두 가지 방법이 있습니다.

  • import foo.*;

    import 문의 수를 잠재적으로 줄입니다.

  • import foo.Bar;

    어떤 클래스가 사용되는지 명확하게 표시하고 관리자가 코드를 더 쉽게 읽을 수 있습니다.

import foo.Bar; 모든 Android 코드를 가져오기 위해. Java 표준 라이브러리( java.util.* , java.io.* 등) 및 단위 테스트 코드( junit.framework.* )에는 명시적인 예외가 있습니다.

자바 라이브러리 규칙

Android의 Java 라이브러리 및 도구를 사용하기 위한 규칙이 있습니다. 경우에 따라 규칙이 중요한 방식으로 변경되어 이전 코드에서 더 이상 사용되지 않는 패턴이나 라이브러리를 사용할 수 있습니다. 이러한 코드로 작업할 때는 기존 스타일을 계속 사용해도 괜찮습니다. 그러나 새 구성 요소를 만들 때 더 이상 사용되지 않는 라이브러리를 사용하지 마십시오.

자바 스타일 규칙

Javadoc 표준 주석 사용

모든 파일은 맨 위에 저작권 표시가 있어야 하고 패키지 및 가져오기 설명(각 블록은 빈 줄로 구분됨), 마지막으로 클래스 또는 인터페이스 선언이 있어야 합니다. Javadoc 주석에서 클래스 또는 인터페이스가 수행하는 작업을 설명하십시오.

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

작성하는 모든 클래스 및 중요한 공용 메소드에는 클래스 또는 메소드가 수행하는 작업을 설명하는 문장이 하나 이상 포함된 Javadoc 주석이 포함 되어야 합니다. 이 문장은 3인칭 서술 동사로 시작해야 합니다.

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

모든 Javadoc이 "sets Foo"라고 말하는 경우 setFoo() 와 같은 사소한 get 및 set 메소드에 대해 Javadoc을 작성할 필요가 없습니다. 메서드가 더 복잡한 작업을 수행하는 경우(예: 제약 조건 적용 또는 중요한 부작용이 있는 경우) 이를 문서화해야 합니다. "Foo" 속성이 의미하는 바가 명확하지 않은 경우 이를 문서화해야 합니다.

public이든 아니든 작성하는 모든 메서드는 Javadoc의 이점을 누릴 수 있습니다. 공용 메소드는 API의 일부이므로 Javadoc이 필요합니다. Android는 Javadoc 주석 작성을 위한 특정 스타일을 적용하지 않지만 How to Write Doc Comments for the Javadoc Tool 의 지침을 따라야 합니다.

짧은 메서드 작성

가능한 경우 메서드를 작고 집중적으로 유지합니다. 때로는 긴 메서드가 적절하다는 것을 알고 있으므로 메서드 길이에 엄격한 제한이 없습니다. 메서드가 40줄 정도를 초과하면 프로그램 구조를 해치지 않고 분할할 수 있는지 생각해 보십시오.

표준 장소에서 필드 정의

파일의 맨 위 또는 필드를 사용하는 메소드 바로 앞에 필드를 정의하십시오.

변수 범위 제한

지역 변수의 범위를 최소로 유지하십시오. 이것은 코드의 가독성과 유지 관리성을 높이고 오류 가능성을 줄입니다. 변수의 모든 사용을 포함하는 가장 안쪽 블록에서 각 변수를 선언합니다.

처음 사용되는 지점에서 지역 변수를 선언합니다. 거의 모든 지역 변수 선언에는 초기화가 포함되어야 합니다. 변수를 합리적으로 초기화할 정보가 아직 충분하지 않은 경우 초기화할 때까지 선언을 연기하세요.

예외는 try-catch 문입니다. 검사 예외를 발생시키는 메서드의 반환 값으로 변수를 초기화하는 경우 try 블록 내에서 초기화해야 합니다. 값을 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 문 자체에서 루프 변수를 선언합니다.

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

그리고

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

주문 수입 명세서

수입 문의 순서는 다음과 같습니다.

  1. 안드로이드 가져오기
  2. 타사( com , junit , net , org )에서 가져오기
  3. javajavax

IDE 설정과 정확히 일치하려면 가져오기가 다음과 같아야 합니다.

  • 소문자 앞에 대문자가 있는 각 그룹 내에서 알파벳순(예: a 앞에 Z)
  • 각 주요 그룹( 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자를 초과하는 리터럴 URL이 포함된 경우 해당 행은 잘라내기 및 붙여넣기를 쉽게 하기 위해 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 주석이 필요한 경우 주석이 적용되는 소프트웨어 요소를 분리하도록 코드를 리팩터링합니다.

약어를 단어로 취급

이름을 더 읽기 쉽게 만들기 위해 약어와 약어를 변수, 메서드 및 클래스 이름 지정에서 단어로 취급합니다.

좋은 나쁜
XmlHttp요청 XMLHTTP요청
getCustomerId getCustomerID
클래스 HTML 클래스 HTML
문자열 URL 문자열 URL
긴 아이디 긴 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 가 "At a future date do something" 형식인 경우 특정 날짜("2005년 11월까지 수정") 또는 특정 이벤트("모든 프로덕션 믹서가 프로토콜 V7을 이해한 후 이 코드를 제거하십시오.")를 포함해야 합니다. ).

드물게 기록

로깅은 필요하지만 성능에 부정적인 영향을 미치며 합리적으로 간결하게 유지하지 않으면 유용성을 잃습니다. 로깅 기능은 5가지 로깅 수준을 제공합니다.

  • 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 개체에 사용되는 경우 + 연산자는 기본 버퍼 크기(16자) 및 잠재적으로 다른 임시 String 개체로 StringBuilder 인스턴스를 암시적으로 만듭니다. 따라서 명시적으로 StringBuilder 개체를 만드는 것은 기본 + 연산자에 의존하는 것보다 비용이 많이 들지 않으며 훨씬 더 효율적일 수 있습니다. Log.v() 를 호출하는 코드는 로그를 읽지 않는 경우에도 문자열 빌드를 포함하여 릴리스 빌드에서 컴파일되고 실행됩니다.
  • 다른 사람이 읽을 수 있고 릴리스 빌드에서 사용할 수 있는 모든 로깅은 모호하지 않고 간결해야 하며 이해할 수 있어야 합니다. 여기에는 DEBUG 수준까지의 모든 로깅이 포함됩니다.
  • 가능하면 한 줄에 계속 로그인하십시오. 줄 길이는 최대 80자 또는 100자까지 허용됩니다. 가능하면 약 130자 또는 160자(태그 길이 포함)보다 긴 길이는 피하십시오.
  • 로깅이 성공하면 VERBOSE 보다 높은 수준에서 사용하지 마십시오.
  • 재현하기 어려운 문제를 진단하기 위해 임시 로깅을 사용하는 경우 DEBUG 또는 VERBOSE 수준으로 유지하고 컴파일 시간에 비활성화할 수 있는 if 블록으로 묶습니다.
  • 로그를 통한 보안 유출에 주의하세요. 개인 정보를 기록하지 마십시오. 특히 보호된 콘텐츠에 대한 정보를 기록하지 마십시오. 이것은 프레임워크 코드를 작성할 때 특히 중요합니다. 개인 정보 또는 보호된 콘텐츠가 될 것인지 여부를 미리 알기가 쉽지 않기 때문입니다.
  • System.out.println() (또는 기본 코드의 경우 printf() 을 사용하지 마십시오. System.outSystem.err/dev/null 로 리디렉션되므로 인쇄 문은 눈에 띄는 효과가 없습니다. 그러나 이러한 호출에 대해 발생하는 모든 문자열 빌드는 여전히 실행됩니다.
  • 로깅의 황금률은 다른 로그가 귀하의 로그를 밀어내지 않는 것처럼 귀하의 로그도 불필요하게 다른 로그를 버퍼 밖으로 밀어내지 않는 것입니다.

자바 테스트 스타일 규칙

테스트 메서드 명명 규칙을 따르고 밑줄을 사용하여 테스트 중인 항목과 테스트 중인 특정 사례를 구분합니다. 이 스타일을 사용하면 어떤 케이스가 테스트되고 있는지 더 쉽게 확인할 수 있습니다. 예를 들어:

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