주석으로 코드 검사 개선

린트와 같은 코드 검사 도구를 사용하면 문제점을 찾아내고 코드를 개선하는 데는 도움이 될 수 있지만 검사 도구가 유추할 수 있는 범위도 한정되어 있습니다. 예를 들어, Android 리소스 ID는 int를 사용해 문자열, 그래픽, 색상 및 기타 리소스 유형을 식별하므로, 색상을 지정했어야 하는 위치에서 문자열 리소스를 지정했더라도 검사 도구가 이 사실을 알려주지는 못합니다. 즉, 코드 검사 기능을 사용하더라도 앱이 잘못 렌더링되거나 아예 실행조차 되지 않을 수도 있습니다.

주석을 사용하면 린트와 같은 코드 검사 도구에 힌트를 줌으로써 보다 미묘한 코드 문제를 더 잘 탐지하도록 할 수 있습니다. 주석은 변수, 매개변수 및 반환 값에 추가하여 메서드 반환 값, 전달된 매개변수, 로컬 변수 및 필드를 검사하는 메타데이터 태그로 추가됩니다. 주석을 코드 검사 도구와 함께 사용하면 null 포인터 예외 및 리소스 유형 충돌과 같은 문제를 감지하는 데 도움이 될 수 있습니다.

Android는 Jetpack Annotations 라이브러리를 통해 다양한 주석을 지원합니다. androidx.annotation 패키지를 통해 이 라이브러리에 액세스할 수 있습니다.

참고: 모듈에 주석 프로세서 종속 항목이 있는 경우 해당 종속 항목을 추가하려면 kapt 또는 ksp 종속 항목 구성(Kotlin)이나 annotationProcessor 종속 항목 구성(Java)을 사용해야 합니다.

프로젝트에 주석 추가하기

프로젝트에서 주석을 사용하려면 라이브러리나 앱에 androidx.annotation:annotation 종속 항목을 추가합니다. 그러면 추가하는 주석에 대해 코드 검사 또는 lint 작업을 실행할 때 검사가 이루어집니다.

Jetpack Annotations 라이브러리 종속 항목 추가

Jetpack Annotations 라이브러리는 Google의 Maven 저장소에 게시됩니다. 프로젝트에 Jetpack Annotations 라이브러리를 추가하려면 build.gradle 또는 build.gradle.kts 파일의 dependencies 블록에 다음 줄을 포함합니다.

Kotlin

dependencies {
    implementation("androidx.annotation:annotation:1.7.1")
}

Groovy

dependencies {
    implementation 'androidx.annotation:annotation:1.7.1'
}
툴바나 화면에 나타나는 동기화 알림 메시지에서 Sync Now를 클릭합니다.

자체 라이브러리 모듈에서 주석을 사용하는 경우에는 주석이 XML 형식으로 된 Android 보관 파일(AAR) 아티팩트의 일부로 annotations.zip 파일에 포함됩니다. androidx.annotation 종속 항목을 추가하더라도 라이브러리의 다운스트림 사용자에 대해서는 종속 항목이 적용되지 않습니다.

참고: 다른 Jetpack 라이브러리를 사용하는 경우 androidx.annotation 종속 항목을 추가할 필요가 없을 수도 있습니다. 다른 많은 Jetpack 라이브러리는 Jetpack Annotations 라이브러리에 종속되므로, 이미 주석에 액세스할 수 있습니다.

Jetpack 저장소에 포함된 주석의 전체 목록은 Jetpack Annotations 라이브러리 참조에서 확인하거나 자동 완성 기능을 사용하여 import androidx.annotation. 명령문에 사용 가능한 옵션을 표시하면 됩니다.

코드 검사 실행하기

Android 스튜디오에서 주석 유효성 검사와 자동 린트 검사가 있는 코드 검사를 시작하려면 메뉴에서 Analyze > Inspect Code를 선택합니다. Android 스튜디오에서는 충돌 메시지를 표시하여 개발자의 코드가 주석과 충돌할 가능성이 있는 문제를 신고하고 가능한 해결 방법을 제안합니다.

명령줄을 사용해 lint 작업을 실행함으로써 주석을 강제 적용할 수도 있습니다. 이 방법은 지속적 통합 서버 관련 문제를 신고하는 데 유용할 수도 있지만, lint 작업은 nullness 주석을 강제 적용하지 않습니다(다음 섹션에서 설명). Android 스튜디오만 강제 적용합니다. 린트 검사의 사용 설정과 실행에 관한 자세한 내용은 린트 검사로 코드 개선을 참고하세요.

주석 충돌로 인해 경고가 생성되더라도 앱 컴파일은 가능합니다.

Nullness 주석

Nullness 주석은 Java 코드에서 값이 null일 수 있는지 여부를 강제 적용하는 데 유용할 수 있습니다. Kotlin 코드에서는 비교적 덜 유용합니다. Kotlin이 컴파일 시 적용되는 null 허용 여부 규칙을 빌드했기 때문입니다.

주어진 변수, 매개변수 또는 반환 값의 nullness를 확인하려면 @Nullable@NonNull 주석을 추가하세요. @Nullable 주석은 null이 될 수 있는 변수, 매개변수 또는 반환 값을 나타냅니다. @NonNull은 null이 될 수 없는 변수, 매개변수 또는 반환 값을 나타냅니다.

예를 들어, null 값을 포함하는 로컬 변수가 특정 메서드에 매개변수로서 전달되고 메서드의 @NonNull 주석이 매개변수에 연결되어 있는 경우 코드를 빌드하면 null이 아닌 충돌을 나타내는 경고가 생성됩니다. 또한 결과가 null인지를 먼저 확인하지 않고 @Nullable로 표시된 메서드의 결과를 참조하려고 하면 nullness 경고가 생성됩니다. 메서드를 사용할 때마다 명시적으로 null 여부를 확인해야 할 경우에는 메서드의 반환 값에 @Nullable만 사용합니다.

다음 예에서는 실제 null 허용 여부를 보여줍니다. Kotlin 예시 코드는 null을 허용하지 않는 유형이 지정되면 생성된 바이트 코드에 자동으로 추가되기 때문에 @NonNull 주석을 활용하지 않습니다. Java 예시에서는 contextattrs 매개변수의 @NonNull 주석을 활용하여 전달된 매개변수 값이 null이 아님을 확인합니다. 또한 onCreateView() 메서드 자체가 null을 반환하지 않는지 확인합니다.

Kotlin

...
    /** Annotation not used because of the safe-call operator(?)**/
    override fun onCreateView(
            name: String?,
            context: Context,
            attrs: AttributeSet
    ): View? {
        ...
    }
...

Java

import androidx.annotation.NonNull;
...
    /** Add support for inflating the <fragment> tag. **/
    @NonNull
    @Override
    public View onCreateView(String name, @NonNull Context context,
      @NonNull AttributeSet attrs) {
      ...
      }
...

Null 허용 여부 분석

Android 스튜디오는 null 허용 여부 분석을 실행하여 코드에서 nullness 주석을 자동으로 유추하고 삽입하는 기능을 지원합니다. null 허용 여부 분석에서는 다음 항목을 찾아내기 위해 코드의 메서드 계층 구조 전반에 걸쳐 계약을 검색합니다.

  • null을 반환할 수 있는 호출 메서드
  • null을 반환해서는 안 되는 메서드
  • null이 될 수 있는 필드, 로컬 변수, 매개변수와 같은 변수
  • null 값을 보유할 수 없는 필드, 로컬 변수, 매개변수와 같은 변수

그런 다음, 발견된 위치에 적절한 null 주석을 자동으로 삽입합니다.

Android 스튜디오에서 null 가능 분석을 실행하려면 Analyze > Infer Nullity를 선택합니다. Android 스튜디오가 코드에서 검색된 위치에 Android @Nullable@NonNull 주석을 삽입합니다. null 분석을 실행한 후에는 삽입된 주석을 확인하는 것이 좋습니다.

참고: nullness 주석을 추가할 때 자동 완성 기능이 Android null 주석 대신 IntelliJ @Nullable@NotNull 주석을 제안할 수 있으며 그에 상응하는 라이브러리를 자동으로 가져올 수 있습니다. 하지만 Android 스튜디오 린트 검사기는 Android null 주석만 찾습니다. 주석을 확인할 때는 린트 검사기가 코드 검사 중에 올바로 알려줄 수 있도록 프로젝트에서 Android null 주석을 사용하는지 확인하세요.

리소스 주석

드로어블, 문자열 리소스와 같은 리소스의 Android 참조는 정수로 전달되기 때문에 리소스 유형의 유효성 검사가 유용할 수 있습니다.

매개변수가 특정 유형의 리소스(예: String)를 참조할 것으로 예상되는 코드는 예상되는 참조 유형인 int로 전달될 수 있지만, 실제로는 R.string 리소스와 같은 다른 리소스 유형을 참조할 수 있습니다.

예를 들어, 아래에 나온 것처럼 @StringRes 주석을 추가하면 리소스 매개변수에 R.string 참조가 포함되어 있는지 검사할 수 있습니다.

Kotlin

abstract fun setTitle(@StringRes resId: Int)

Java

public abstract void setTitle(@StringRes int resId)

코드 검사 중 매개변수에 R.string 참조가 전달되지 않으면 주석에서 경고가 생성됩니다.

@DrawableRes, @DimenRes, @ColorRes, @InterpolatorRes 같은 다른 리소스 유형의 주석도 같은 주석 형식을 사용해 추가하고 코드 검사 중에 실행할 수 있습니다.

매개변수가 여러 리소스 유형을 지원할 경우 주어진 매개변수에 리소스 유형 주석을 두 개 이상 배치할 수 있습니다. 주석이 지정된 매개변수가 임의의 R 리소스 유형일 수 있음을 나타내려면 @AnyRes를 사용합니다.

@ColorRes를 사용하면 매개변수가 색상 리소스여야 함을 지정할 수 있습니다. 하지만, 색상 정수(RRGGBB 또는 AARRGGBB 형식)는 색상 리소스로 인식되지 않습니다. 대신, @ColorInt 주석을 사용해야 매개변수가 색상 정수여야 함을 나타낼 수 있습니다. 빌드 도구는 색상 정수가 아니라 android.R.color.black과 같은 색상 리소스 ID를 주석이 지정된 메서드로 전달하는 잘못된 코드를 플래그 지정합니다.

스레드 주석

스레드 주석은 메서드가 특정 유형의 스레드로부터 호출되는지 확인합니다. 다음과 같은 스레드 주석이 지원됩니다.

빌드 도구는 @MainThread@UiThread 주석을 교환 가능한 것으로 취급하므로, @MainThread 메서드에서 @UiThread 메서드를 호출할 수 있고 그 반대 방향으로도 호출할 수 있습니다. 하지만 서로 다른 스레드의 뷰가 여러 개 있는 시스템 앱의 경우 UI 스레드가 주 스레드와 다를 가능성이 있습니다. 따라서 @UiThread가 있는 앱의 뷰 계층 구조와 연결된 메서드에 주석을 달고 @MainThread가 있는 앱의 수명 주기와 연결된 메서드에만 주석을 달아야 합니다.

클래스에 있는 모든 메서드의 스레딩 요구사항이 동일하다면 클래스에 단일 스레드 주석을 추가하여 클래스의 모든 메서드가 같은 유형의 스레드로부터 호출되는지 확인할 수 있습니다.

스레드 주석의 일반적인 용도는 @WorkerThread 주석이 달린 메서드나 클래스가 적절한 백그라운드 스레드에서만 호출되는지 확인하는 것입니다.

값 제약 조건 주석

전달된 매개변수 값의 유효성 검사를 실행하려면 @IntRange, @FloatRange@Size 주석을 사용하세요. @IntRange@FloatRange는 사용자가 잘못된 범위를 지정할 가능성이 있는 매개변수에 적용할 때 가장 유용합니다.

@IntRange 주석은 integer 또는 long 형식의 매개변수 값이 지정 범위 내에 있는지 확인합니다. 다음 예는 alpha 매개변수가 0~255의 정수 값을 포함해야 함을 나타냅니다.

Kotlin

fun setAlpha(@IntRange(from = 0, to = 255) alpha: Int) { ... }

Java

public void setAlpha(@IntRange(from=0,to=255) int alpha) { ... }

@FloatRange 주석은 float 또는 double 형식의 매개변수 값이 부동 소수점 값의 지정 범위 내에 있는지 검사합니다. 다음 예는 alpha 매개변수에 0.0~1.0의 부동 소수점 값을 포함해야 함을 나타냅니다.

Kotlin

fun setAlpha(@FloatRange(from = 0.0, to = 1.0) alpha: Float) {...}

Java

public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {...}

@Size 주석은 컬렉션 또는 배열의 크기나 문자열의 길이를 검사합니다. @Size 주석을 사용하여 다음과 같은 특성을 확인할 수 있습니다.

  • 최소 크기(예: @Size(min=2))
  • 최대 크기(예: @Size(max=2))
  • 정확한 크기(예: @Size(2))
  • 크기가 배수여야 하는 숫자(예: @Size(multiple=2))

예를 들어, @Size(min=1)은 컬렉션이 비어 있지 않은지 검사하고 @Size(3)은 배열에 정확히 3개의 값이 있는지 확인합니다.

다음 예는 location 배열에 요소를 1개 이상 포함해야 함을 나타냅니다.

Kotlin

fun getLocation(button: View, @Size(min=1) location: IntArray) {
    button.getLocationOnScreen(location)
}

Java

void getLocation(View button, @Size(min=1) int[] location) {
    button.getLocationOnScreen(location);
}

권한 주석

메서드 호출자 권한의 유효성을 검사하려면 @RequiresPermission 주석을 사용합니다. 유효한 권한의 목록에서 단일 권한이 있는지 검사하려면 anyOf 속성을 사용하고 권한 집합이 있는지 검사하려면 allOf 속성을 사용합니다. 다음 예에서는 setWallpaper() 메서드에 주석을 달아 메서드 호출자에게 permission.SET_WALLPAPERS 권한이 있어야 함을 나타냅니다.

Kotlin

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
@Throws(IOException::class)
abstract fun setWallpaper(bitmap: Bitmap)

Java

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;

다음 예에서는 copyImageFile() 메서드의 호출자에게 외부 저장소의 읽기 권한과 복사된 이미지의 위치 메타데이터의 읽기 권한이 모두 있어야 합니다.

Kotlin

@RequiresPermission(allOf = [
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.ACCESS_MEDIA_LOCATION
])
fun copyImageFile(dest: String, source: String) {
    ...
}

Java

@RequiresPermission(allOf = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.ACCESS_MEDIA_LOCATION})
public static final void copyImageFile(String dest, String source) {
    //...
}

인텐트 관련 권한의 경우 인텐트 작업 이름을 정의하는 문자열 필드에 권한 요건을 지정합니다.

Kotlin

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
const val ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"

Java

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

읽기와 쓰기 액세스에 개별 권한이 필요한 콘텐츠 제공업체 관련 권한의 경우 각 권한 요건을 @RequiresPermission.Read 또는 @RequiresPermission.Write 주석에 래핑하세요.

Kotlin

@RequiresPermission.Read(RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(RequiresPermission(WRITE_HISTORY_BOOKMARKS))
val BOOKMARKS_URI = Uri.parse("content://browser/bookmarks")

Java

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");

간접 권한

권한이 메서드의 매개변수에 제공되는 특정 값에 종속될 때는 특정 권한을 나열하지 않고 매개변수 자체에서 @RequiresPermission을 사용합니다. 예를 들어 startActivity(Intent) 메서드는 자체에 전달되는 인텐트에 간접 권한을 사용합니다.

Kotlin

abstract fun startActivity(@RequiresPermission intent: Intent, bundle: Bundle?)

Java

public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle)

간접 권한을 사용할 때는 빌드 도구가 데이터 흐름 분석을 실행하여 메서드로 전달되는 인수에 @RequiresPermission 주석이 있는지 확인합니다. 그런 다음, 매개변수에서 기존 주석을 메서드 자체에 강제 적용합니다. startActivity(Intent) 예에서 적절한 권한이 없는 인텐트가 메서드로 전달되면 Intent 클래스의 주석 때문에 그림 1처럼 startActivity(Intent)의 잘못된 사용을 알리는 경고가 발생합니다.

그림 1. startActivity(Intent) 메서드의 간접 권한 주석에서 생성되는 경고

빌드 도구는 Intent 클래스에 있는 상응하는 인텐트 작업 이름의 주석에서 startActivity(Intent) 관련 경고를 생성합니다.

Kotlin

@RequiresPermission(Manifest.permission.CALL_PHONE)
const val ACTION_CALL = "android.intent.action.CALL"

Java

@RequiresPermission(Manifest.permission.CALL_PHONE)
public static final String ACTION_CALL = "android.intent.action.CALL";

필요하다면 메서드의 매개변수에 주석을 달 때 @RequiresPermission.Read 또는 @RequiresPermission.Write 대신 @RequiresPermission을 사용할 수 있습니다. 하지만 간접 권한의 경우 읽기 또는 쓰기 권한 주석 중 어느 하나와 함께 @RequiresPermission을 사용해서는 안 됩니다.

반환 값 주석

@CheckResult 주석을 사용하여 메서드의 결과 또는 반환 값이 실제로 사용되는지 확인합니다. 무효가 아닌 모든 메서드에 @CheckResult 주석을 다는 대신에 혼동을 줄 수 있는 메서드의 결과를 분명히 밝히는 주석을 추가합니다.

예를 들어, 초보 Java 개발자는 주로 <String>.trim()이 원래 문자열에서 공백을 제거한다고 착각합니다. 메서드에 @CheckResult 주석을 달면 <String>.trim() 사용 플래그가 지정되며, 이때는 호출자가 메서드의 반환 값으로 아무것도 하지 않습니다.

다음은 메서드의 반환 값이 실제로 참조되는지 확인하기 위해 checkPermissions() 메서드에 주석을 다는 예시입니다. 이 예는 또한 개발자에게 대체 메서드로 제안할 메서드로 enforcePermission() 메서드를 지정합니다.

Kotlin

@CheckResult(suggest = "#enforcePermission(String,int,int,String)")
abstract fun checkPermission(permission: String, pid: Int, uid: Int): Int

Java

@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public abstract int checkPermission(@NonNull String permission, int pid, int uid);

CallSuper 주석

재정의하는 메서드가 메서드의 슈퍼 구현을 호출하는지 확인하려면 @CallSuper 주석을 사용합니다.

다음은 재정의하는 모든 메서드 구현이 super.onCreate()를 호출하도록 하기 위해 onCreate() 메서드에 주석을 다는 예시입니다.

Kotlin

@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
}

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Typedef 주석

Typedef 주석은 특정 매개변수, 반환 값, 필드가 특정 상수 집합을 참조하는지 확인합니다. 또한 허용되는 상수를 자동으로 제공하도록 코드 완성을 사용 설정합니다.

@IntDef@StringDef 주석을 사용하면 정수 및 문자열 집합으로 구성된 열거형 주석을 생성하여 다른 유형의 코드 참조에 대한 유효성을 검사할 수 있습니다.

Typedef 주석은 @interface를 사용하여 새로운 열거형 주석 형식을 선언합니다. @IntDef@StringDef 주석은 @Retention과 함께 새 주석을 생성하는데, 이러한 주석은 열거 형식을 정의하는 데 필요합니다. @Retention(RetentionPolicy.SOURCE) 주석은 컴파일러에 .class 파일에 열거형 주석 데이터를 저장하지 않도록 지시합니다.

다음은 메서드 매개변수로 전달된 값이 정의된 상수 중 하나를 참조하는지 확인하는 주석을 만드는 단계를 보여주는 예시입니다.

Kotlin

import androidx.annotation.IntDef
//...
// Define the list of accepted constants and declare the NavigationMode annotation.
@Retention(AnnotationRetention.SOURCE)
@IntDef(NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS)
annotation class NavigationMode

// Declare the constants.
const val NAVIGATION_MODE_STANDARD = 0
const val NAVIGATION_MODE_LIST = 1
const val NAVIGATION_MODE_TABS = 2

abstract class ActionBar {

    // Decorate the target methods with the annotation.
    // Attach the annotation.
    @get:NavigationMode
    @setparam:NavigationMode
    abstract var navigationMode: Int

}

Java

import androidx.annotation.IntDef;
//...
public abstract class ActionBar {
    //...
    // Define the list of accepted constants and declare the NavigationMode annotation.
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
    public @interface NavigationMode {}

    // Declare the constants.
    public static final int NAVIGATION_MODE_STANDARD = 0;
    public static final int NAVIGATION_MODE_LIST = 1;
    public static final int NAVIGATION_MODE_TABS = 2;

    // Decorate the target methods with the annotation.
    @NavigationMode
    public abstract int getNavigationMode();

    // Attach the annotation.
    public abstract void setNavigationMode(@NavigationMode int mode);
}

이 코드를 빌드할 때 mode 매개변수가 정의된 상수 NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS 중 하나를 참조하지 않는 경우 경고가 생성됩니다.

@IntDef@IntRange를 결합하여 정수가 특정 상수 집합이거나 범위 내의 값일 수 있음을 나타냅니다.

상수와 플래그 결합 사용 설정하기

사용자가 허용되는 상수를 플래그(예: |, &, ^ 등)와 결합할 수 있는 경우, 매개변수 또는 반환 값이 유효한 패턴을 참조하는지 검사하는 flag 속성을 가진 주석을 정의할 수 있습니다.

다음은 유효한 DISPLAY_ 상수의 목록을 포함한 DisplayOptions 주석을 생성하는 예시입니다.

Kotlin

import androidx.annotation.IntDef
...

@IntDef(flag = true, value = [
    DISPLAY_USE_LOGO,
    DISPLAY_SHOW_HOME,
    DISPLAY_HOME_AS_UP,
    DISPLAY_SHOW_TITLE,
    DISPLAY_SHOW_CUSTOM
])
@Retention(AnnotationRetention.SOURCE)
annotation class DisplayOptions
...

Java

import androidx.annotation.IntDef;
...

@IntDef(flag=true, value={
        DISPLAY_USE_LOGO,
        DISPLAY_SHOW_HOME,
        DISPLAY_HOME_AS_UP,
        DISPLAY_SHOW_TITLE,
        DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {}

...

주석 플래그가 있는 코드를 빌드할 때 데코레이트된 매개변수 또는 반환 값이 유효한 패턴을 참조하지 않을 경우 경고가 생성됩니다.

Keep 주석

@Keep 주석은 빌드 시점에 코드가 축소될 때 주석 지정된 클래스나 메서드가 삭제되지 않도록 합니다. 일반적으로 이 주석은 리플렉션을 통해 액세스되는 메서드 및 클래스에 추가되어 컴파일러가 코드를 사용되지 않는 것으로 취급하지 않도록 합니다.

주의: @Keep을 사용해 주석을 다는 클래스와 메서드는 앱의 로직 내에서 이 클래스 및 메서드를 참조하지 않는 경우에도 항상 앱의 APK에 표시됩니다.

앱 크기를 작게 유지하려면 앱의 각 @Keep 주석을 보존해야 하는지 고려합니다. 리플렉션을 사용하여 주석 지정된 클래스나 메서드에 액세스하는 경우 ProGuard 규칙에서 -if 조건문을 사용하여 리플렉션 호출을 하는 클래스를 지정합니다.

코드를 축소하는 방법 및 삭제해서는 안 되는 코드를 지정하는 방법에 관한 자세한 내용은 앱 축소, 난독화 및 최적화를 참고하세요.

코드 가시성 주석

코드의 특정 부분(예: 메서드, 클래스, 필드, 패키지)의 가시성을 지정하려면 다음과 같은 주석을 사용합니다.

테스트용 코드 표시

@VisibleForTesting 주석은 주석 지정된 메서드를 테스트하기 쉽게 만들기 위해 메서드의 가시성을 일반적으로 필요한 수준보다 더 높게 만듭니다. 이 주석에는 테스트를 위해 가시적으로 만들어야 할 필요가 없었다면 메서드 가시성을 어느 수준으로 할지 지정할 수 있는 선택적인 otherwise 인수가 있습니다. 린트에서는 otherwise 인수를 사용하여 원하는 가시성을 강제 적용할 수 있습니다.

다음 예에서 myMethod()는 일반적으로 private이지만 테스트의 경우 package-private입니다. VisibleForTesting.PRIVATE를 지정하면 private 액세스 권한에서 허용되는 컨텍스트 외부로부터(예: 다른 컴파일 단위에서) 이 메서드가 호출되는 경우 린트에서 메시지가 표시됩니다.

Kotlin

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun myMethod() {
    ...
}

Java

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
void myMethod() { ... }

@VisibleForTesting(otherwise = VisibleForTesting.NONE)을 지정하여 테스트용으로만 존재한다는 메서드임을 나타낼 수도 있습니다. 이 형식은 @RestrictTo(TESTS)를 사용하는 것과 같습니다. 두 경우 모두 동일한 린트 검사를 실행합니다.

API 제한하기

@RestrictTo 주석은 주석 지정된 API (패키지, 클래스, 메서드) 액세스가 다음과 같이 제한된다는 것을 나타냅니다.

서브클래스

API 액세스를 서브클래스로만 제한하려면 주석 형식 @RestrictTo(RestrictTo.Scope.SUBCLASSES)를 사용합니다.

주석 지정된 클래스를 확장하는 클래스만 이 API에 액세스할 수 있습니다. Java protected 수정자는 동일 패키지 내의 관련 없는 클래스에서 액세스하는 것을 허용하므로 제한성이 부족합니다. 또한, 이전에 protected로 지정했거나 재정의한 메서드를 public으로 지정할 수 없기 때문에 나중에 유연하게 사용하기 위해 메서드를 public으로 두는 동시에 클래스는 클래스 내부에서만 사용하거나 서브클래스에서만 사용할 것이라는 힌트를 제공하려는 경우가 있습니다.

라이브러리

API 액세스를 라이브러리만으로 제한하려면 주석 형식 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)를 사용합니다.

라이브러리 코드만 주석 지정된 API에 액세스할 수 있습니다. 따라서 개발자는 원하는 패키지 계층 구조로 자유롭게 코드를 구성할 뿐 아니라 관련 라이브러리 그룹 사이에 코드를 공유할 수도 있습니다. 이 옵션은 외부 용도는 아니지만 보완되는 다양한 Jetpack 라이브러리에서 공유하기 위해 public이어야 하는 구현 코드가 많은 Jetpack 라이브러리에서 이미 사용할 수 있습니다.

테스트

다른 개발자가 나의 테스트 API에 액세스하지 못하게 하려면 주석 형식 @RestrictTo(RestrictTo.Scope.TESTS)를 사용하세요.

테스트 코드만 주석 지정된 API에 액세스할 수 있습니다. 이렇게 하면 내가 테스트 목적으로만 사용하는 API를 다른 개발자가 개발용으로 사용할 수 없습니다.