Ulepsz inspekcję kodu za pomocą adnotacji

Korzystanie z narzędzi do inspekcji kodu, takich jak lint, może pomóc w wykrywaniu problemów i ulepszaniu kodu, ale narzędzia do inspekcji nie są w stanie wyciągnąć zbyt wielu wniosków. Na przykład identyfikatory zasobów Androida używają wartości int do oznaczania ciągów znaków, grafik, kolorów i innych typów zasobów, więc narzędzia do sprawdzania nie mogą stwierdzić, kiedy zdefiniowano zasób ciągu znaków, a kiedy kolor. W takim przypadku aplikacja może być renderowana nieprawidłowo lub wcale się nie uruchomić, nawet jeśli używasz inspekcji kodu.

Dzięki adnotacjom możesz przekazywać wskazówki narzędziom do inspekcji kodu, takim jak lint, aby wykrywać te bardziej subtelne problemy z kodem. Adnotacje są dodawane jako tagi metadanych, które możesz dołączać do zmiennych, parametrów i wartości zwracanych, aby sprawdzać wartości zwracane przez metody, przekazywane parametry, zmienne lokalne i pola. W połączeniu z narzędziami do sprawdzania kodu adnotacje mogą pomóc w wykrywaniu problemów takich jak wyjątki związane z wskaźnikiem null i konflikty typu zasobu.

Android obsługuje różne adnotacje za pomocą biblioteki adnotacji Jetpacka. Do biblioteki możesz uzyskać dostęp za pomocą pakietu androidx.annotation.

Uwaga: jeśli moduł jest zależny od procesora adnotacji, musisz użyć konfiguracji zależności kapt lub ksp w przypadku Kotlina albo konfiguracji zależności annotationProcessor w przypadku Javy, aby dodać tę zależność.

Dodawanie adnotacji do projektu

Aby włączyć adnotacje w projekcie, dodaj do biblioteki lub aplikacji zależność androidx.annotation:annotation. Dodane adnotacje są sprawdzane podczas inspekcji kodu lub zadania lint.

Dodaj zależność biblioteki Jetpack Annotations

Biblioteka adnotacji Jetpack jest publikowana w repozytorium Maven Google. Aby dodać do projektu bibliotekę Jetpack Anotations, dodaj ten wiersz do bloku dependencies w pliku build.gradle lub build.gradle.kts:

Kotlin

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

Groovy

dependencies {
    implementation 'androidx.annotation:annotation:1.9.1'
}
Następnie na pasku narzędzi lub w pojawiającym się powiadomieniu o synchronizacji kliknij Synchronizuj teraz.

Jeśli w swoim własnym module biblioteki używasz adnotacji, są one uwzględniane w ramach artefaktu Android Archive (AAR) w formacie XML w pliku annotations.zip. Dodanie zależności androidx.annotation nie powoduje zależności dla użytkowników biblioteki.

Uwaga: jeśli używasz innych bibliotek Jetpacka, możesz nie musieć dodawać zależności androidx.annotation. Wiele innych bibliotek Jetpacka zależy od biblioteki adnotacji, więc być może masz już dostęp do adnotacji.

Pełną listę adnotacji dostępnych w repozytorium Jetpacka znajdziesz w pliku referencyjnym biblioteki adnotacji Jetpacka lub możesz użyć funkcji autouzupełniania, aby wyświetlić dostępne opcje dla instrukcji import androidx.annotation..

Przeprowadzanie inspekcji kodu

Aby rozpocząć sprawdzanie kodu w Android Studio, które obejmuje sprawdzanie poprawności adnotacji i automatyczne sprawdzanie błędów, w menu kliknij Analizuj > Sprawdzanie kodu. Android Studio wyświetla komunikaty o konflikcie, aby sygnalizować potencjalne problemy, w których Twój kod jest niezgodny z adnotacjami, oraz sugerować możliwe rozwiązania.

Możesz też wymusić adnotacje, uruchamiając zadanie lint za pomocą wiersza poleceń. Chociaż może to być przydatne do oznaczania problemów z serwerem ciągłej integracji, zadanie lint nie wymusza adnotacji nullości (opisanych w następnej sekcji); robi to tylko Android Studio. Więcej informacji o włączaniu i uruchamianiu kontroli lint znajdziesz w artykule Ulepszanie kodu za pomocą kontroli lint.

Konflikty adnotacji generują ostrzeżenia, ale nie uniemożliwiają kompilacji aplikacji.

Adnotacje dotyczące wartości NULL

W kodzie Java adnotacje nullości mogą być przydatne do wymuszania, czy wartości mogą być null. W kodzie Kotlina są one mniej przydatne, ponieważ Kotlin ma wbudowane reguły dotyczące możliwości wystąpienia wartości null, które są egzekwowane w czasie kompilacji.

Dodaj adnotacje @Nullable@NonNull, aby sprawdzić, czy dana zmienna, parametr lub wartość zwracana jest pusta. Adnotacja @Nullable wskazuje zmienną, parametr lub wartość zwracaną, która może być pusta. @NonNull wskazuje zmienną, parametr lub wartość zwracaną, która nie może być pusta.

Jeśli na przykład zmienna lokalna zawierająca wartość null jest przekazywana jako parametr do metody z dołączoną adnotacją @NonNull, kompilacja kodu powoduje ostrzeżenie o konflikcie z wartością inną niż null. Próba odwołania się do wyniku metody oznaczonej symbolem @Nullable bez wcześniejszego sprawdzenia, czy wynik jest pusty, powoduje ostrzeżenie o wartości null. Używaj @Nullable do wartości zwracanej przez metodę tylko wtedy, gdy każde użycie metody musi być wyraźnie sprawdzane pod kątem wartości null.

Poniższy przykład pokazuje, jak działa opcja dopuszczenia wartości null. Przykładowy kod Kotlina nie korzysta z adnotacji @NonNull, ponieważ jest ona automatycznie dodawana do wygenerowanego kodu bajtowego, gdy określony jest typ, który nie może być pusty. Przykład w języku Java korzysta z adnotacji @NonNull w przypadku parametrów contextattrs, aby sprawdzić, czy przekazane wartości parametrów nie są puste. Sprawdza też, czy metoda onCreateView() nie zwraca wartości 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) {
      ...
      }
...

Analiza dopuszczalności

Android Studio umożliwia przeprowadzanie analizy możliwości wystąpienia wartości null, aby automatycznie wywnioskować i wstawić adnotacje nullości w kodzie. Analiza możliwości wystąpienia błędu skanuje umowy w hierarchiach metod w Twoim kodzie, aby wykryć:

  • Wywoływanie metod, które mogą zwracać wartość null.
  • Metody, które nie powinny zwracać wartości null.
  • Zmienne, takie jak pola, zmienne lokalne i parametry, które mogą mieć wartość null.
  • Zmienne, takie jak pola, zmienne lokalne i parametry, które nie mogą mieć wartości null.

Następnie analiza automatycznie wstawia odpowiednie adnotacje null w wykrytych lokalizacjach.

Aby przeprowadzić analizę możliwości wystąpienia wartości null w Android Studio, kliknij Analizuj > Przyjmij wartość null. Android Studio wstawia adnotacje Androida @Nullable@NonNull w wykrytych lokalizacjach w Twoim kodzie. Po przeprowadzeniu analizy pustych wartości warto zweryfikować wstrzyknięte adnotacje.

Uwaga: podczas dodawania adnotacji nullości autouzupełnianie może sugerować adnotacje IntelliJ @Nullable i @NotNull zamiast adnotacji nullości na Androida. Może też automatycznie zaimportować odpowiednią bibliotekę. Sprawdzacz lint w Android Studio sprawdza jednak tylko adnotacje null na Androida. Podczas sprawdzania adnotacji sprawdź, czy Twój projekt używa adnotacji null na Androida, aby sprawdzacz lint mógł odpowiednio poinformować Cię o błędach podczas inspekcji kodu.

Adnotacje zasobów

Walidowanie typów zasobów może być przydatne, ponieważ odwołania do zasobów w Androidzie, takich jak zasoby drawablestring, są przekazywane jako liczby całkowite.

Kod, który oczekuje, że parametr odwołuje się do określonego typu zasobu, np. String, może zostać przekazany do oczekiwanego typu odwołania int, ale tak naprawdę odwołuje się do innego typu zasobu, np. zasobu R.string.

Na przykład dodaj adnotacje @StringRes, aby sprawdzić, czy parametr zasobu zawiera odwołanie R.string, jak pokazano tutaj:

Kotlin

abstract fun setTitle(@StringRes resId: Int)

Java

public abstract void setTitle(@StringRes int resId)

Podczas sprawdzania kodu adnotacja generuje ostrzeżenie, jeśli w parametrze nie jest przekazywane odwołanie R.string.

Adnotacje dla innych typów zasobów, takich jak @DrawableRes, @DimenRes, @ColorRes@InterpolatorRes, można dodawać w tym samym formacie adnotacji i uruchamiać podczas inspekcji kodu.

Jeśli parametr obsługuje wiele typów zasobów, możesz dodać do niego więcej niż jedną adnotację typu zasobu. Użyj @AnyRes, aby wskazać, że adnotowany parametr może być dowolnym typem zasobu R.

Chociaż możesz użyć atrybutu @ColorRes, aby określić, że parametr powinien być zasobem koloru, liczba całkowita koloru (w formacie RRGGBB lub AARRGGBB) nie jest rozpoznawana jako zasób koloru. Zamiast tego użyj adnotacji @ColorInt, aby wskazać, że parametr musi być liczbą całkowitą koloru. Narzędzia do kompilacji będą oznaczać nieprawidłowy kod, który przekazuje do metody opatrzonej adnotacjami identyfikator zasobu koloru, np. android.R.color.black, zamiast koloru całkowitego.

Adnotacje wątków

Adnotacje wątku sprawdzają, czy metoda jest wywoływana z określonego typu wątku. Obsługiwane są te adnotacje wątku:

Narzędzia do kompilacji traktują adnotacje @MainThread@UiThread jako wymienne, więc możesz wywoływać metody @UiThread z metod @MainThread i odwrotnie. W przypadku aplikacji systemowych z wieloma widokami w różnych wątkach może się jednak zdarzyć, że wątek interfejsu użytkownika będzie inny niż główny wątek. Dlatego metody powiązane z hierarchią widoku aplikacji należy opatrzyć adnotacjami @UiThread, a metody powiązane z cyklem życia aplikacji – adnotacjami @MainThread.

Jeśli wszystkie metody w klasie mają te same wymagania dotyczące wątków, możesz dodać do klasy pojedynczą adnotację wątku, aby sprawdzić, czy wszystkie metody w klasie są wywoływane z tego samego typu wątku.

Adnotacje wątku są często używane do sprawdzania, czy metody lub klasy oznaczone adnotacją @WorkerThread są wywoływane tylko z odpowiedniego wątku w tle.

Adnotacje ograniczeń wartości

Użyj adnotacji @IntRange, @FloatRange i @Size, aby zweryfikować wartości przekazanych parametrów. Zarówno @IntRange, jak i @FloatRange są najbardziej przydatne w przypadku parametrów, w których przypadku użytkownicy mogą podać nieprawidłowy zakres.

Adnotacja @IntRange sprawdza, czy wartość parametru typu integer lub long mieści się w określonym zakresie. Z tego przykładu wynika, że parametr alpha musi zawierać wartość całkowitą z zakresu od 0 do 255:

Kotlin

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

Java

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

Adnotacja @FloatRange sprawdza, czy wartość parametru typu float lub double mieści się w określonym zakresie wartości zmiennoprzecinkowych. Z tego przykładu wynika, że parametr alpha musi zawierać wartość zmiennoprzecinkową z zakresu od 0,0 do 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) {...}

Adnotacja @Size sprawdza rozmiar kolekcji lub tablicy albo długość ciągu znaków. Adnotację @Size można wykorzystać do weryfikacji tych właściwości:

  • Minimalny rozmiar, np. @Size(min=2)
  • Maksymalny rozmiar, np. @Size(max=2)
  • Dokładny rozmiar, np. @Size(2)
  • Liczba, której wielokrotnością musi być rozmiar, np. @Size(multiple=2)

Na przykład @Size(min=1)sprawdza, czy kolekcja nie jest pusta, a @Size(3)sprawdza, czy tablica zawiera dokładnie 3 wartości.

Ten przykład pokazuje, że tablica location musi zawierać co najmniej 1 element:

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

adnotacje dotyczące uprawnień;

Użyj adnotacji @RequiresPermission, aby zweryfikować uprawnienia wywołującego metodę. Aby sprawdzić, czy na liście prawidłowych uprawnień znajduje się określone uprawnienie, użyj atrybutu anyOf. Aby sprawdzić zestaw uprawnień, użyj atrybutu allOf. W tym przykładzie metoda setWallpaper() jest opatrzona adnotacją, która wskazuje, że wywołujący tę metodę musi mieć uprawnienie 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;

W tym przykładzie wywołujący metodę copyImageFile() musi mieć dostęp do odczytu z zewnętrznej pamięci masowej oraz dostęp do odczytu metadanych lokalizacji w skopiowanym obrazie:

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

W przypadku uprawnień dotyczących intencji umieść wymagane uprawnienia w polu ciągu znaków, które definiuje nazwę działania intencji:

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

W przypadku uprawnień dostawców treści, które wymagają oddzielnych uprawnień do odczytu i zapisu, owiń każde z nich w annotację @RequiresPermission.Read lub @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");

Uprawnienia pośrednie

Jeśli uprawnienia zależą od konkretnej wartości podanej w parametrze metody, użyj @RequiresPermission na samym parametrze, nie wymieniając konkretnych uprawnień. Na przykład metoda startActivity(Intent) używa pośredniego uprawnienia w intencji przekazanej do metody:

Kotlin

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

Java

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

Gdy używasz uprawnień pośrednich, narzędzia do kompilacji przeprowadzają analizę przepływu danych, aby sprawdzić, czy argument przekazany do metody zawiera adnotacje @RequiresPermission. Następnie stosuje ona wszystkie adnotacje z parametru w samej metodzie. W przykładzie startActivity(Intent) adnotacje w klasie Intent powodują ostrzeżenia o nieprawidłowym użyciu funkcji startActivity(Intent), gdy do metody przekazywany jest zamiar bez odpowiednich uprawnień, jak pokazano na rysunku 1.

Rysunek 1. Ostrzeżenie wygenerowane z bezpośredniej adnotacji uprawnień w metodie startActivity(Intent).

Narzędzia do kompilacji generują ostrzeżenie w startActivity(Intent) na podstawie adnotacji dotyczącej nazwy działania w intencji w klasie 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";

W razie potrzeby możesz zastąpić @RequiresPermission wartością @RequiresPermission.Read lub @RequiresPermission.Write podczas adnotowania parametru metody. W przypadku uprawnień pośrednich @RequiresPermission nie należy jednak używać w połączeniu z adnotacją uprawnień do odczytu ani uprawnień do zapisu.

Adnotacje zwracanej wartości

Użyj adnotacji @CheckResult, aby sprawdzić, czy wynik metody lub zwracana wartość są rzeczywiście używane. Zamiast oznaczać @CheckResult każdą niepusta metodę, dodaj adnotację, aby wyjaśnić wyniki potencjalnie mylących metod.

Na przykład początkujący programiści Java często błędnie uważają, że znak <String>.trim() usuwa spacje z pierwotnego ciągu znaków. Dodanie do metody oznaczenia @CheckResult, która używa flag <String>.trim(), gdy wywołujący nie robi nic z wartością zwracaną metody.

W tym przykładzie metoda checkPermissions() jest opatrzona adnotacją, aby sprawdzić, czy odwołuje się do wartości zwracanej przez tę metodę. Zawiera też nazwę metody enforcePermission(), która zostanie zaproponowana deweloperowi jako zamiennik:

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

Adnotacje CallSuper

Aby sprawdzić, czy metoda zastępująca wywołuje implementację metody nadrzędnej, użyj adnotacji @CallSuper.

W tym przykładzie metoda onCreate() jest opatrzona adnotacjami, aby zapewnić, że wszystkie implementacje metody zastępczej wywołują metodę super.onCreate():

Kotlin

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

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Adnotacje typu

Adnotacje typu definiują, czy dany parametr, zwracana wartość lub pole odwołuje się do określonego zbioru stałych. Umożliwiają też automatyczne uzupełnianie kodu, aby automatycznie oferować dozwolone stałe.

Użyj adnotacji @IntDef i @StringDef, aby utworzyć adnotacje z wyliczeniami zbiorów liczb całkowitych i ciągów znaków, które służą do sprawdzania innych typów odwołań do kodu.

Adnotacje typu definicji używają instrukcji @interface do zadeklarowania nowego typu adnotacji z wyliczeniem. Adnotacje @IntDef i @StringDef wraz z adnotacją @Retention adnotują nową adnotację i są niezbędne do zdefiniowania typu wyliczeniowego. Adnotacja @Retention(RetentionPolicy.SOURCE) informuje kompilator, aby nie przechowywać ponumerowanych danych adnotacji w pliku .class.

W tym przykładzie pokazano, jak utworzyć adnotację, która sprawdza, czy wartość przekazana jako parametr metody odwołuje się do jednej z zdefiniowanych stałych:

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

Podczas kompilowania tego kodu generowane jest ostrzeżenie, jeśli parametr mode nie odwołuje się do jednej z zdefiniowanych stałych (NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST lub NAVIGATION_MODE_TABS).

Połącz @IntDef@IntRange, aby wskazać, że liczba całkowita może być określonym zbiorem stałych lub wartością w zakresie.

Włączanie łączenia stałych wartości z flagami

Jeśli użytkownicy mogą łączyć dozwolone stałe z flagą (np. |, &, ^ itd.), możesz zdefiniować adnotację za pomocą atrybutu flag, aby sprawdzić, czy parametr lub wartość zwracana odwołuje się do prawidłowego wzorca.

Ten przykład tworzy adnotację DisplayOptions z listą prawidłowych stałych DISPLAY_:

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

...

Gdy kompilujesz kod z flagą adnotacji, generowane jest ostrzeżenie, jeśli ozdobiony parametr lub wartość zwracana nie odwołuje się do prawidłowego wzorca.

Zachowaj adnotację

Adnotacja @Keepzapewnia, że adnotowana klasa lub metoda nie zostanie usunięta, gdy kod zostanie zminiaturyzowany w czasie kompilacji. Ta adnotacja jest zwykle dodawana do metod i klas, do których dostęp uzyskuje się za pomocą odbicia lustrzanego, aby zapobiec traktowaniu kodu jako nieużywanego przez kompilator.

Uwaga: klasy i metody, które opatrujesz adnotacjami za pomocą @Keep, zawsze pojawiają się w pliku APK aplikacji, nawet jeśli nigdy nie odwołujesz się do tych klas i metod w logice aplikacji.

Aby zmniejszyć rozmiar aplikacji, zastanów się, czy konieczne jest zachowanie w niej każdej adnotacji @Keep. Jeśli używasz odbicia lustrzanego do uzyskiwania dostępu do adnotowanej klasy lub metody, użyj w regułach ProGuard instrukcji warunkowej -if, określając klasę, która wykonuje wywołania odbicia lustrzanego.

Więcej informacji o minifikacji kodu i określaniu, którego kodu nie należy usuwać, znajdziesz w artykule Zmniejsz, zaciemnij i zoptymalizuj aplikację.

Adnotacje dotyczące widoczności kodu

Aby oznaczyć widoczność określonych części kodu, takich jak metody, klasy, pola czy pakiety, użyj tych adnotacji.

Udostępnianie kodu na potrzeby testowania

Adnotacja @VisibleForTesting wskazuje, że adnotowana metoda jest bardziej widoczna niż zwykle, co ułatwia jej testowanie. Ta adnotacja ma opcjonalny argument otherwise, który pozwala określić, jaka byłaby widoczność metody, gdyby nie potrzeba jej wyświetlania na potrzeby testowania. Lint używa argumentu otherwise, aby narzucić wymaganą widoczność.

W tym przykładzie wartość myMethod() to zwykle private, ale w przypadku testów jest to package-private. W przypadku oznaczenia VisibleForTesting.PRIVATE lint wyświetla komunikat, jeśli ta metoda jest wywoływana spoza kontekstu dozwolonego przez dostęp private, na przykład z innego modułu kompilacji.

Kotlin

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

Java

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

Możesz też podać @VisibleForTesting(otherwise = VisibleForTesting.NONE), aby wskazać, że metoda istnieje tylko do testowania. Ten formularz jest taki sam jak @RestrictTo(TESTS). Oba wykonują te same kontrole.

Ograniczanie interfejsu API

Adnotacja @RestrictTo wskazuje, że dostęp do oznaczonego interfejsu API (pakietu, klasy lub metody) jest ograniczony w ten sposób:

Podklasy

Aby ograniczyć dostęp do interfejsu API tylko do podklas, użyj formularza adnotacji @RestrictTo(RestrictTo.Scope.SUBCLASSES).

Dostęp do tego interfejsu API mają tylko klasy, które rozszerzają adnotowaną klasę. Modyfikator Java protected nie jest wystarczająco restrykcyjny, ponieważ zezwala na dostęp z niepowiązanych klas w tym samym pakiecie. Są też przypadki, w których chcesz pozostawić metodę public, aby zapewnić sobie większą elastyczność w przyszłości, ponieważ nie możesz utworzyć metody protected, która byłaby zastąpiona metodą public, ale chcesz podać wskazówkę, że klasa jest przeznaczona tylko do użytku w ramach klasy lub tylko z podklas.

Biblioteki

Aby ograniczyć dostęp interfejsu API tylko do Twoich bibliotek, użyj formularza adnotacji @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX).

Do interfejsu API z adnotacjami może mieć dostęp tylko kod Twojej biblioteki. Dzięki temu możesz nie tylko organizować kod w dowolnej hierarchii pakietu, ale też udostępniać go grupie powiązanych bibliotek. Ta opcja jest już dostępna w przypadku bibliotek Jetpacka, które zawierają dużo kodu implementacji, który nie jest przeznaczony do użytku zewnętrznego, ale musi być public, aby udostępnić go w różnych dodatkowych bibliotekach Jetpacka.

Testowanie

Aby uniemożliwić innym programistom dostęp do interfejsów API do testowania, użyj formularza adnotacji @RestrictTo(RestrictTo.Scope.TESTS).

Do interfejsu API z adnotacjami dostęp ma tylko kod testowy. Zapobiega to używaniu przez innych programistów interfejsów API do celów programistycznych, które mają służyć tylko do testowania.