Ulepsz inspekcję kodu za pomocą adnotacji

Korzystanie z narzędzi do sprawdzania kodu, takich jak lint, może pomóc w znalezieniu problemów i ulepszeniu kodu, ale narzędzia te mogą tylko w pewnym stopniu wywnioskować, co jest nie tak. Identyfikatory zasobów Androida, np. int, służą do identyfikowania ciągów tekstowych, grafiki, kolorów i innych typów zasobów, więc narzędzia do sprawdzania nie mogą stwierdzić, czy w miejscu, w którym powinien być kolor, został podany zasób w postaci ciągu tekstowego. W takiej sytuacji aplikacja może być renderowana nieprawidłowo lub w ogóle nie działać, nawet jeśli sprawdzisz kod.

Adnotacje umożliwiają przekazywanie wskazówek do narzędzi do sprawdzania kodu, takich jak lint, aby ułatwić wykrywanie tych bardziej subtelnych problemów z kodem. Adnotacje są dodawane jako tagi metadanych, które dołączasz 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 wskaźnika pustego i konflikty typów zasobów.

Android obsługuje różne adnotacje za pomocą biblioteki adnotacji Jetpack. Biblioteka jest dostępna w androidx.annotation pakiecie.

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

Dodawanie adnotacji do projektu

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

Dodaj zależność biblioteki Jetpack Annotations

Biblioteka Jetpack Annotations jest publikowana w repozytorium Maven Google. Aby dodać do projektu bibliotekę Jetpack Annotations, umieść ten wiersz w bloku dependencies 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 powiadomieniu o synchronizacji, które się pojawi, kliknij Synchronizuj teraz.

Jeśli używasz adnotacji we własnym module biblioteki, są one dołączane do artefaktu Android Archive (AAR) w formacie XML w pliku annotations.zip. Dodanie zależności androidx.annotation nie powoduje wprowadzenia zależności dla żadnych użytkowników biblioteki.

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

Pełną listę adnotacji zawartych w repozytorium Jetpack znajdziesz w dokumentacji biblioteki adnotacji Jetpack lub używając funkcji autouzupełniania, aby wyświetlić dostępne opcje instrukcji import androidx.annotation..

Przeprowadzanie inspekcji kodu

Aby rozpocząć sprawdzanie kodu w Android Studio, które obejmuje weryfikację adnotacji i automatyczne sprawdzanie za pomocą narzędzia lint, w menu wybierz Analyze (Analizuj) > Inspect Code (Sprawdź kod). Android Studio wyświetla komunikaty o konfliktach, aby sygnalizować potencjalne problemy, w których kod jest sprzeczny z adnotacjami, i sugerować możliwe rozwiązania.

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

Chociaż konflikty adnotacji generują ostrzeżenia, nie uniemożliwiają one kompilacji aplikacji.

Adnotacje dotyczące wartości null

Adnotacje dotyczące wartości null mogą być przydatne w kodzie Java, aby określić, czy wartości mogą być wartościami null. Są one mniej przydatne w kodzie Kotlin, ponieważ Kotlin ma wbudowane reguły dopuszczalności wartości null, które są egzekwowane w czasie kompilacji.

Dodaj adnotacje @Nullable@NonNull, aby sprawdzić, czy dana zmienna, parametr lub wartość zwracana mają wartość null. Adnotacja @Nullable wskazuje zmienną, parametr lub wartość zwracaną, która może mieć wartość null. Symbol @NonNull oznacza zmienną, parametr lub wartość zwracaną, która nie może mieć wartości null.

Jeśli na przykład zmienna lokalna zawierająca wartość null jest przekazywana jako parametr do metody z adnotacją @NonNull dołączoną do tego parametru, podczas kompilowania kodu generowane jest ostrzeżenie wskazujące konflikt wartości null. Próba odwołania się do wyniku metody oznaczonej adnotacją @Nullable bez wcześniejszego sprawdzenia, czy wynik jest wartością null, generuje ostrzeżenie o wartości null. Używaj tylko @Nullable w przypadku wartości zwracanej przez metodę, jeśli każde użycie metody musi być jawnie sprawdzane pod kątem wartości null.

Poniższy przykład pokazuje, jak działa możliwość przyjmowania wartości null. Przykładowy kod w języku Kotlin nie korzysta z adnotacji @NonNull, ponieważ jest ona automatycznie dodawana do wygenerowanego kodu bajtowego, gdy określony jest typ niepusty. W przykładzie w języku Java użyto adnotacji @NonNull w przypadku parametrów contextattrs, aby sprawdzić, czy przekazane wartości parametrów nie są wartościami null. Sprawdza też, czy sama 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 wartości null

Android Studio obsługuje przeprowadzanie analizy dopuszczalności wartości null, aby automatycznie wywnioskować i wstawić w kodzie adnotacje dotyczące dopuszczalności wartości null. Analiza dopuszczalności wartości null skanuje kontrakty w hierarchiach metod w Twoim kodzie, aby wykryć:

  • Metody wywoływania, 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ą przechowywać wartości null.

Następnie analiza automatycznie wstawia odpowiednie adnotacje o wartości null w wykrytych lokalizacjach.

Aby uruchomić analizę dopuszczalności wartości null w Android Studio, kliknij Analyze (Analizuj) > Infer Nullity (Wywnioskuj dopuszczalność wartości null). Android Studio wstawia adnotacje @Nullable@NonNull w wykrytych lokalizacjach w kodzie. Po przeprowadzeniu analizy zerowej warto sprawdzić wstawione adnotacje.

Uwaga: podczas dodawania adnotacji o wartości null funkcja autouzupełniania może sugerować adnotacje IntelliJ @Nullable@NotNull zamiast adnotacji Androida o wartości null i może automatycznie importować odpowiednią bibliotekę. Narzędzie do lintowania w Android Studio sprawdza jednak tylko adnotacje o wartości null w Androidzie. Podczas weryfikacji adnotacji upewnij się, że projekt korzysta z adnotacji o wartości null w Androidzie, aby narzędzie do sprawdzania kodu mogło prawidłowo powiadamiać Cię podczas inspekcji kodu.

Adnotacje do zasobów

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

Kod, który oczekuje, że parametr będzie odwoływać się do określonego typu zasobu, np. String, może zostać przekazany do oczekiwanego typu odwołania int, ale w rzeczywistości odwoływać 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 zostanie przekazane R.stringodwołanie.

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

Jeśli parametr obsługuje wiele typów zasobów, możesz umieścić w nim 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ć @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ą reprezentującą kolor. Narzędzia do kompilacji będą oznaczać nieprawidłowy kod, który przekazuje do metod z adnotacjami identyfikator zasobu koloru, np. android.R.color.black, zamiast liczby całkowitej koloru.

Adnotacje do wątków

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

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 wątek interfejsu może być inny niż wątek główny. Dlatego metody powiązane z hierarchią widoków aplikacji należy oznaczać adnotacją @UiThread, a metody powiązane z cyklem życia aplikacji – adnotacją @MainThread.

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

Często używa się adnotacji wątków do sprawdzania, czy metody lub klasy oznaczone adnotacją @WorkerThread są wywoływane tylko z odpowiedniego wątku w tle.

Adnotacje dotyczące ograniczeń wartości

Użyj adnotacji @IntRange, @FloatRange@Size, aby sprawdzić wartości przekazywanych parametrów. Zarówno @IntRange, jak i @FloatRange są najbardziej przydatne w przypadku parametrów, w których użytkownicy mogą się pomylić.

Adnotacja @IntRange sprawdza, czy wartość parametru typu integer lub long mieści się w określonym zakresie. Poniższy przykład wskazuje, że parametr alpha musi zawierać liczbę 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 zmiennoprzecinkowego lub podwójnej precyzji mieści się w określonym zakresie wartości zmiennoprzecinkowych. Poniższy przykład wskazuje, ż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. Adnotacji @Size można użyć do weryfikacji tych cech:

  • 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) weryfikuje, czy tablica zawiera dokładnie 3 wartości.

Poniższy przykład wskazuje, ż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 osoby wywołującej metodę. Aby sprawdzić pojedyncze uprawnienie z listy prawidłowych uprawnień, użyj atrybutu anyOf. Aby sprawdzić zestaw uprawnień, użyj atrybutu allOf. W poniższym przykładzie metoda setWallpaper() jest opatrzona adnotacją wskazującą, ż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;

Poniższy przykład wymaga, aby wywołujący metodę copyImageFile() miał dostęp do odczytu zarówno zewnętrznej pamięci masowej, jak i 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ń do intencji umieść wymaganie dotyczące uprawnień w polu tekstowym, 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ń do dostawców treści, którzy wymagają oddzielnych uprawnień do odczytu i zapisu, umieść każde wymaganie dotyczące uprawnień w adnotacji @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 uprawnienie zależy od konkretnej wartości przekazanej do parametru metody, użyj @RequiresPermission w przypadku samego parametru bez podawania konkretnych uprawnień. Na przykład metoda startActivity(Intent) używa uprawnienia pośredniego w przypadku 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 ma adnotacje @RequiresPermission. Następnie wymusza wszelkie istniejące adnotacje z parametru w samej metodzie. W startActivity(Intent)przykładzie adnotacje w klasie Intent powodują wyświetlanie ostrzeżeń o nieprawidłowym użyciu startActivity(Intent), gdy do metody przekazywany jest zamiar bez odpowiednich uprawnień, jak pokazano na rysunku 1.

Rysunek 1. Ostrzeżenie wygenerowane na podstawie adnotacji dotyczącej uprawnień pośrednich w metodzie startActivity(Intent).

Narzędzia do kompilacji generują ostrzeżenie w przypadku startActivity(Intent) na podstawie adnotacji w odpowiedniej nazwie działania 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 znakiem @RequiresPermission.Read lub @RequiresPermission.Write podczas dodawania adnotacji do parametru metody. W przypadku uprawnień pośrednich symbolu @RequiresPermission nie należy jednak używać w połączeniu z adnotacjami dotyczącymi uprawnień do odczytu lub zapisu.

Adnotacje wartości zwracanej

Użyj adnotacji @CheckResult, aby sprawdzić, czy wynik lub wartość zwracana metody jest rzeczywiście używana. Zamiast dodawać adnotację @CheckResult do każdej metody, która nie jest typu void, dodaj ją, aby wyjaśnić wyniki potencjalnie mylących metod.

Na przykład nowi programiści w Javie często błędnie myślą, że funkcja <String>.trim() usuwa białe znaki z oryginalnego ciągu znaków. Dodanie do metody adnotacji z flagą @CheckResult oznacza użycie <String>.trim(), gdy wywołujący nie robi nic z wartością zwracaną przez metodę.

W przykładzie poniżej użyto adnotacji w metodzie checkPermissions(), aby sprawdzić, czy wartość zwracana przez tę metodę jest rzeczywiście używana. Wskazuje też enforcePermission() metodę, którą należy zaproponować 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

Użyj adnotacji @CallSuper, aby sprawdzić, czy metoda zastępująca wywołuje implementację superklasy.

W poniższym przykładzie metoda onCreate() jest opatrzona adnotacją, aby mieć pewność, że wszystkie implementacje zastępujące metodę wywołują super.onCreate():

Kotlin

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

Java

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Adnotacje typedef

Adnotacje typedef sprawdzają, czy dany parametr, wartość zwracana lub pole odwołuje się do określonego zestawu stałych. Umożliwiają też automatyczne uzupełnianie kodu, dzięki czemu wyświetlają dozwolone stałe.

Używaj adnotacji @IntDef@StringDef do tworzenia wyliczeniowych adnotacji zbiorów liczb całkowitych i ciągów znaków w celu weryfikowania innych typów odwołań do kodu.

Adnotacje typedef używają słowa kluczowego @interface do deklarowania nowego typu adnotacji wyliczeniowej. Adnotacje @IntDef@StringDef oraz @Retention służą do oznaczania nowej adnotacji i są niezbędne do zdefiniowania typu wyliczeniowego. Adnotacja @Retention(RetentionPolicy.SOURCE) informuje kompilator, aby nie przechowywał wyliczonych danych adnotacji w pliku .class.

W przykładzie poniżej pokazujemy, jak utworzyć adnotację, która sprawdza, czy wartość przekazana jako parametr metody odwołuje się do jednej ze 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 tworzenia tego kodu generowane jest ostrzeżenie, jeśli parametr mode nie odwołuje się do jednej ze zdefiniowanych stałych (NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST lub NAVIGATION_MODE_TABS).

Połącz symbole @IntDef@IntRange, aby wskazać, że liczba całkowita może być danym zestawem stałych lub wartością z zakresu.

Włączanie łączenia stałych z flagami

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

W tym przykładzie tworzymy 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 tworzysz kod z flagą adnotacji, generowane jest ostrzeżenie, jeśli dekorowany parametr lub zwracana wartość nie odwołuje się do prawidłowego wzorca.

Zachowaj adnotację

Adnotacja @Keep sprawia, że adnotowane zajęcia lub metoda nie są usuwane, gdy kod jest minimalizowany w czasie kompilacji. Ta adnotacja jest zwykle dodawana do metod i klas, do których uzyskuje się dostęp za pomocą odbicia, aby zapobiec traktowaniu kodu przez kompilator jako nieużywanego.

Ostrzeżenie: klasy i metody, które oznaczasz za pomocą adnotacji @Keep, zawsze pojawiają się w pliku APK aplikacji, nawet jeśli nigdy nie odwołujesz się do nich w logice aplikacji.

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

Więcej informacji o tym, jak zminimalizować kod i określić, które fragmenty nie mają być usuwane, znajdziesz w artykule Zmniejszanie, zaciemnianie i optymalizowanie aplikacji.

Adnotacje dotyczące widoczności kodu

Używaj tych adnotacji, aby oznaczać widoczność określonych części kodu, takich jak metody, klasy, pola lub pakiety.

Udostępnianie kodu do testowania

Adnotacja @VisibleForTesting wskazuje, że adnotowana metoda jest bardziej widoczna niż zwykle, aby można było ją przetestować. Ta adnotacja ma opcjonalny argument otherwise, który pozwala określić, jaka byłaby widoczność metody, gdyby nie trzeba było jej udostępniać na potrzeby testowania. Lint używa argumentu otherwise, aby wymusić zamierzoną widoczność.

W tym przykładzie myMethod() to zwykle private, ale w przypadku testów jest to package-private. Oznaczenie VisibleForTesting.PRIVATE sprawia, że narzędzie lint wyświetla komunikat, jeśli ta metoda jest wywoływana spoza kontekstu dozwolonego przez dostęp private, np. z innej jednostki kompilacji.

Kotlin

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

Java

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

Możesz też określić @VisibleForTesting(otherwise = VisibleForTesting.NONE), aby wskazać, że metoda istnieje tylko na potrzeby testowania. Ten formularz jest taki sam jak w przypadku użycia @RestrictTo(TESTS). Oba wykonują to samo sprawdzanie.

Ograniczanie interfejsu API

Adnotacja @RestrictTo oznacza, że dostęp do danego interfejsu API (pakietu, klasy lub metody) jest ograniczony w następujący sposób:

Podklasy

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

Tylko klasy rozszerzające klasę z adnotacjami mogą uzyskać dostęp do tego interfejsu API. Modyfikator Java protected nie jest wystarczająco restrykcyjny, ponieważ umożliwia dostęp z niepowiązanych klas w tym samym pakiecie. Istnieją też przypadki, w których chcesz pozostawić metodę public, aby zapewnić sobie większą elastyczność w przyszłości, ponieważ nie możesz nigdy utworzyć wcześniej protected i zastąpić metody public, ale chcesz zasugerować, że klasa jest przeznaczona do użycia tylko w klasie lub w podklasach.

Biblioteki

Użyj formularza adnotacji @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX), aby ograniczyć dostęp do interfejsu API tylko do swoich bibliotek.

Tylko kod biblioteki może mieć dostęp do interfejsu API z adnotacjami. Dzięki temu możesz nie tylko organizować kod w dowolnej hierarchii pakietów, ale też udostępniać go w grupie powiązanych bibliotek. Ta opcja jest już dostępna w bibliotekach Jetpack, które zawierają dużo kodu implementacji nieprzeznaczonego do użytku zewnętrznego, ale który musi być public, aby można było udostępniać go w różnych uzupełniających się bibliotekach Jetpack.

Testowanie

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

Dostęp do interfejsu API z adnotacjami ma tylko kod testowy. Uniemożliwi to innym programistom korzystanie z interfejsów API do celów programistycznych, jeśli zamierzasz używać ich tylko do testowania.