Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

주석으로 코드 검사 개선

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

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

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

참고: 모듈에 주석 프로세서 종속성이 있는 경우 'annotationProcessor' 종속성 구성을 사용하여 이 종속성을 추가해야 합니다. 자세한 내용은 주석 프로세서 종속성 구성 사용을 참조하세요.

프로젝트에 주석 추가하기

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

지원 주석 라이브러리 종속성 추가하기

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

    dependencies {
        implementation 'com.android.support:support-annotations:28.0.0'
    }
    
툴바나 화면에 나타나는 동기화 알림 메시지에서 Sync Now를 클릭합니다.

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

참고: appcompat 라이브러리를 사용 중인 경우에는 support-annotations 종속성을 추가해야 합니다. appcompat 라이브러리는 이미 주석 라이브러리에 종속되므로, 주석에 액세스할 수 있습니다.

지원 저장소에 포함된 주석의 전체 목록을 보려면 Support Annotations 라이브러리 참조를 확인하거나 자동 완성 기능을 사용하여 import android.support.annotation. 문에 사용 가능한 옵션을 표시합니다.

코드 검사 실행하기

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

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

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

Nullness 주석

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

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

다음 예에서는 @NonNull 주석을 contextattrs 매개변수에 연결하여 전달된 매개변수 값이 null이 아님을 확인합니다. 또한, 다음과 같이 onCreateView() 메서드 자체가 null을 반환하지 않는다는 점도 확인합니다. Kotlin으로 작업할 때는 null 사용 불가능한 유형을 지정하면 생성된 바이트코드에 자동으로 추가되기 때문에 @NonNull 주석을 사용할 필요가 없습니다.

Kotlin

    import android.support.annotation.NonNull
    ...

        /** Add support for inflating the <fragment> tag. **/
        fun onCreateView(
                name: String?,
                context: Context,
                attrs: AttributeSet
        ): View? {
            ...
        }
    ...
    

자바

    import android.support.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 @Nullable@NonNull 주석이 삽입됩니다. Null 분석을 실행한 후에는 삽입된 주석을 확인하는 것이 좋습니다.

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

리소스 주석

드로어블 리소스, 문자열 리소스와 같은 리소스의 Android 참조는 정수로 전달되기 때문에 리소스 유형의 유효성 검사가 유용할 수 있습니다. 매개변수가 특정 유형의 리소스를 참조할 것으로 예상되는 코드(예: 드로어블)는 예상되는 참조 유형인 int로 전달될 수 있지만, 실제로는 R.string 리소스와 같은 다른 리소스 유형을 참조할 수 있습니다.

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

Kotlin

    abstract fun setTitle(@StringRes resId: Int)
    

자바

    public abstract void setTitle(@StringRes int resId)
    

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

@DrawableRes, @DimenRes, @ColorRes, @InterpolatorRes 같은 다른 리소스 유형의 주석도 같은 주석 형식을 사용해 추가하고 코드 검사 중에 실행할 수 있습니다. 매개변수가 여러 리소스 유형을 지원할 경우 주어진 매개변수에 이들 주석 중 두 개 이상을 배치할 수 있습니다. 주석이 지정된 매개변수가 임의의 R 리소스 유형일 수 있음을 나타내려면 @AnyRes를 사용합니다.

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

스레드 주석

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

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

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

스레드 주석의 공통적인 용도는 AsyncTask 클래스에서 메서드의 재정의를 확인하는 것입니다. 이 클래스가 백그라운드 작업을 수행하고 UI 스레드에 결과만 게시하기 때문입니다.

값 제약 조건 주석

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

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

Kotlin

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

자바

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

자바

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

자바

    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)
    

자바

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

다음 예에서는 copyFile() 메서드의 호출자에게 외부 저장소의 읽기 권한과 쓰기 권한을 모두 요구합니다.

Kotlin

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

자바

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

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

Kotlin

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

자바

    @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")
    

자바

    @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?)
    

자바

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

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

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

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

Kotlin

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

자바

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

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

반환 값 주석

메서드의 결과 또는 반환 값이 실제로 사용되는지 확인하려면 @CheckResult 주석을 사용합니다. 무효가 아닌 모든 메서드에 @CheckResult로 주석을 다는 대신, 혼동을 일으킬 가능성이 있는 메서드의 결과를 분명히 밝히는 주석을 추가합니다. 예를 들어, 초보 자바 개발자는 <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
    

자바

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

자바

    @CallSuper
    protected void onCreate(Bundle savedInstanceState) {
    }
    

Typedef 주석

@IntDef@StringDef 주석을 사용하면 정수 및 문자열 집합으로 구성된 열거형 주석을 만들어 다른 유형의 코드 참조의 유효성을 검사할 수 있습니다. Typedef 주석은 특정 매개변수, 반환 값, 필드가 특정 상수 집합을 참조하도록 합니다. 또한, 허용되는 상수를 자동으로 제공하도록 코드 완성을 사용 설정합니다.

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

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

Kotlin

    import android.support.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

    }
    

자바

    import android.support.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 android.support.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
    ...
    

자바

    import android.support.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 인수가 있습니다. Lint에서는 otherwise 인수를 사용하여 원하는 가시성을 강제 적용할 수 있습니다.

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

Kotlin

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

자바

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

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

API 제한하기

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

서브클래스

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

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

라이브러리

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

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

참고: Android 지원 라이브러리 클래스 및 패키지는 이제 @RestrictTo(GROUP_ID)로 주석이 달립니다. 다시 말해, 실수로 이 구현 클래스를 사용하는 경우 Lint에서는 이를 사용하지 않도록 경고합니다.

테스트

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

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