Улучшите проверку кода с помощью аннотаций

Использование инструментов проверки кода, таких как lint , может помочь вам обнаружить проблемы и улучшить код, но возможности инструментов проверки ограничены. Например, идентификаторы ресурсов Android используют тип int для обозначения строк, графики, цветов и других типов ресурсов, поэтому инструменты проверки не могут определить, когда вы указали строковый ресурс вместо цвета. Это означает, что ваше приложение может отображаться некорректно или вообще не запускаться, даже если вы используете проверку кода.

Аннотации позволяют предоставлять подсказки инструментам проверки кода, таким как lint, для выявления более тонких проблем. Аннотации добавляются в виде метатегов, которые прикрепляются к переменным, параметрам и возвращаемым значениям для проверки возвращаемых значений методов, переданных параметров, локальных переменных и полей. При использовании с инструментами проверки кода аннотации могут помочь обнаружить такие проблемы, как исключения нулевого указателя и конфликты типов ресурсов.

Android поддерживает различные аннотации через библиотеку аннотаций Jetpack . Доступ к библиотеке осуществляется через пакет androidx.annotation .

Примечание: Если модуль зависит от процессора аннотаций, необходимо использовать конфигурацию зависимости kapt или ksp для Kotlin или конфигурацию зависимости annotationProcessor для Java, чтобы добавить эту зависимость.

Добавьте аннотации к вашему проекту

Чтобы включить аннотации в своём проекте, добавьте зависимость androidx.annotation:annotation в свою библиотеку или приложение. Все добавляемые вами аннотации проверяются при запуске проверки кода или lint .

Добавьте зависимость библиотеки аннотаций Jetpack

Библиотека аннотаций Jetpack опубликована в репозитории Maven от Google . Чтобы добавить библиотеку аннотаций Jetpack в свой проект, добавьте следующую строку в блок dependencies файла build.gradle или build.gradle.kts :

Котлин

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

Круто

dependencies {
    implementation 'androidx.annotation:annotation:1.9.1'
}
Затем на панели инструментов или в появившемся уведомлении о синхронизации нажмите Синхронизировать сейчас .

Если вы используете аннотации в собственном модуле библиотеки, они включаются в состав артефакта Android Archive (AAR) в формате XML в файле annotations.zip . Добавление зависимости androidx.annotation не приводит к появлению зависимости для последующих пользователей вашей библиотеки.

Примечание: Если вы используете другие библиотеки Jetpack, вам может не потребоваться добавлять зависимость androidx.annotation . Поскольку многие другие библиотеки Jetpack зависят от библиотеки аннотаций, у вас может уже быть доступ к этим аннотациям.

Полный список аннотаций, включенных в репозиторий Jetpack, можно найти в справочнике по библиотеке аннотаций Jetpack или воспользоваться функцией автозаполнения, чтобы отобразить доступные параметры для оператора import androidx.annotation.

Проведение проверок кода

Чтобы запустить проверку кода в Android Studio, которая включает проверку аннотаций и автоматическую проверку линта, выберите в меню «Анализ» > «Проверка кода» . Android Studio отображает сообщения о конфликтах, чтобы отметить потенциальные проблемы, возникающие при конфликте вашего кода с аннотациями, и предложить возможные решения.

Вы также можете принудительно применять аннотации, запустив задачу lint из командной строки . Хотя это может быть полезно для выявления проблем с сервером непрерывной интеграции, задача lint не обеспечивает принудительное применение аннотаций nullness (описанных в следующем разделе); это делает только Android Studio. Подробнее о включении и запуске lint-инспекций см. в разделе « Улучшение кода с помощью lint-проверок» .

Хотя конфликты аннотаций приводят к появлению предупреждений, эти предупреждения не мешают компиляции вашего приложения.

Аннотации об отсутствии

Аннотации об отсутствии значений могут быть полезны в коде Java для контроля допустимости значений NULL. В коде Kotlin они менее полезны, поскольку в Kotlin есть встроенные правила допустимости NULL, которые применяются во время компиляции.

Добавьте аннотации @Nullable и @NonNull , чтобы проверить, является ли заданная переменная, параметр или возвращаемое значение пустым. Аннотация @Nullable указывает на переменную, параметр или возвращаемое значение, которые могут быть пустыми. @NonNull указывает на переменную, параметр или возвращаемое значение, которые не могут быть пустыми.

Например, если локальная переменная, содержащая значение NULL, передается в качестве параметра методу с прикрепленной к этому параметру аннотацией @NonNull , сборка кода генерирует предупреждение о конфликте значений, отличных от NULL. Кроме того, попытка сослаться на результат метода, помеченного аннотацией @Nullable , без предварительной проверки результата на значение NULL приводит к предупреждению о ненулевом значении. Используйте @Nullable для возвращаемого значения метода только в том случае, если каждое использование метода должно явно проверяться на значение NULL.

Следующий пример демонстрирует возможность значения NULL в действии. В примере кода на Kotlin аннотация @NonNull не используется, поскольку она автоматически добавляется к сгенерированному байт-коду при указании типа, не допускающего значение NULL. В примере на Java аннотация @NonNull используется для параметров context и attrs для проверки того, что переданные значения параметров не равны NULL. Он также проверяет, что сам метод onCreateView() не возвращает значение NULL:

Котлин

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

Ява

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

Анализ на неопределенность

Android Studio поддерживает анализ на допустимость значений NULL для автоматического определения и добавления аннотаций на допустимость значений NULL в код. Анализ на допустимость значений NULL сканирует контракты по всей иерархии методов в коде для выявления:

  • Вызов методов, которые могут возвращать null.
  • Методы, которые не должны возвращать значение null.
  • Переменные, такие как поля, локальные переменные и параметры, которые могут быть нулевыми.
  • Переменные, такие как поля, локальные переменные и параметры, которые не могут содержать нулевое значение.

Затем анализ автоматически вставляет соответствующие нулевые аннотации в обнаруженные места.

Чтобы выполнить анализ на допустимость значений NULL в Android Studio, выберите «Анализ» > «Вывести значение NULL» . Android Studio вставляет аннотации @Nullable и @NonNull в обнаруженные места кода. После анализа на допустимость значений NULL рекомендуется проверить внедрённые аннотации.

Примечание: При добавлении аннотаций, указывающих на наличие null, функция автодополнения может предложить аннотации IntelliJ @Nullable и @NotNull вместо аннотаций Android null и автоматически импортировать соответствующую библиотеку. Однако линтинг-проверщик Android Studio ищет только аннотации Android null. При проверке аннотаций убедитесь, что ваш проект использует аннотации Android null, чтобы линтинг-проверщик мог корректно уведомлять вас при проверке кода.

Аннотации ресурсов

Проверка типов ресурсов может быть полезна, поскольку ссылки Android на ресурсы, такие как ресурсы рисования и строковые ресурсы, передаются как целые числа.

Код, который ожидает, что параметр будет ссылаться на определенный тип ресурса, например String , может быть передан ожидаемому ссылочному типу int , но фактически ссылаться на другой тип ресурса, например ресурс R.string .

Например, добавьте аннотации @StringRes , чтобы проверить, содержит ли параметр ресурса ссылку R.string , как показано здесь:

Котлин

abstract fun setTitle(@StringRes resId: Int)

Ява

public abstract void setTitle(@StringRes int resId)

Во время проверки кода аннотация генерирует предупреждение, если в параметре не передана ссылка на R.string .

Аннотации для других типов ресурсов, таких как @DrawableRes , @DimenRes , @ColorRes и @InterpolatorRes , можно добавлять с использованием того же формата аннотаций и запускать во время проверки кода.

Если ваш параметр поддерживает несколько типов ресурсов, вы можете добавить к нему несколько аннотаций типов ресурсов. Используйте @AnyRes , чтобы указать, что аннотируемый параметр может быть любым типом ресурса R

Хотя вы можете использовать @ColorRes для указания того, что параметр должен быть цветовым ресурсом, целочисленное значение цвета (в формате RRGGBB или AARRGGBB ) не распознаётся как цветовой ресурс. Вместо этого используйте аннотацию @ColorInt , чтобы указать, что параметр должен быть целочисленным значением цвета. Инструменты сборки будут отмечать некорректный код, который передаёт аннотированным методам идентификатор цветового ресурса, например android.R.color.black , вместо целочисленного значения цвета.

Аннотации к темам

Аннотации потоков проверяют, вызывается ли метод из потока определённого типа. Поддерживаются следующие аннотации потоков:

Инструменты сборки рассматривают аннотации @MainThread и @UiThread как взаимозаменяемые, поэтому вы можете вызывать методы @UiThread из методов @MainThread и наоборот. Однако поток пользовательского интерфейса может отличаться от основного потока, например, в случае системных приложений с несколькими представлениями в разных потоках. Поэтому следует аннотировать методы, связанные с иерархией представлений приложения, с помощью @UiThread , а аннотировать только методы, связанные с жизненным циклом приложения, с помощью @MainThread .

Если все методы в классе имеют одинаковые требования к потокам, вы можете добавить в класс аннотацию одного потока, чтобы убедиться, что все методы в классе вызываются из одного и того же типа потока.

Обычно аннотации потоков используются для проверки того, что методы или классы, аннотированные @WorkerThread , вызываются только из соответствующего фонового потока.

Аннотации ограничений значений

Используйте аннотации @IntRange , @FloatRange и @Size для проверки значений переданных параметров. Аннотации @IntRange и @FloatRange наиболее полезны при применении к параметрам, диапазон которых пользователи могут определить неверно.

Аннотация @IntRange подтверждает, что целочисленное или длинное значение параметра находится в указанном диапазоне. В следующем примере показано, что параметр alpha должен содержать целое значение от 0 до 255:

Котлин

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

Ява

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

Аннотация @FloatRange проверяет, находится ли значение параметра типа float или double в указанном диапазоне значений с плавающей точкой. В следующем примере показано, что параметр alpha должен содержать значение типа float от 0,0 до 1,0:

Котлин

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) подтверждает, что массив содержит ровно три значения.

В следующем примере показано, что массив location должен содержать хотя бы один элемент:

Котлин

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 :

Котлин

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

В следующем примере требуется, чтобы вызывающая сторона метода copyImageFile() имела как доступ на чтение к внешнему хранилищу, так и доступ на чтение к метаданным местоположения в скопированном изображении:

Котлин

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

Ява

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

Для разрешений на намерения поместите требование разрешения в строковое поле, которое определяет имя действия намерения:

Котлин

@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 :

Котлин

@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) использует косвенное разрешение на интент, переданный методу:

Котлин

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

Ява

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

При использовании косвенных разрешений инструменты сборки выполняют анализ потока данных, чтобы проверить, содержит ли аргумент, переданный в метод, аннотации @RequiresPermission . Затем они применяют все существующие аннотации из параметра к самому методу. В примере startActivity(Intent) аннотации в классе Intent приводят к появлению предупреждений о недопустимом использовании startActivity(Intent) , когда методу передается интент без соответствующих разрешений, как показано на рисунке 1.

Рисунок 1. Предупреждение, сгенерированное из аннотации косвенных разрешений для метода startActivity(Intent) .

Инструменты сборки генерируют предупреждение о startActivity(Intent) из аннотации к соответствующему имени действия намерения в классе Intent :

Котлин

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

Ява

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

При необходимости вы можете заменить @RequiresPermission на @RequiresPermission.Read или @RequiresPermission.Write при аннотации параметра метода. Однако для косвенных разрешений @RequiresPermission не следует использовать вместе с аннотациями разрешений на чтение или запись.

Аннотации возвращаемых значений

Используйте аннотацию @CheckResult для проверки того, что результат или возвращаемое значение метода действительно используются. Вместо того, чтобы аннотировать каждый непустой метод аннотацией @CheckResult , добавьте эту аннотацию для пояснения результатов потенциально запутанных методов.

Например, начинающие разработчики Java часто ошибочно полагают, что метод < String >.trim() удаляет пробелы из исходной строки. Аннотирование метода @CheckResult указывает на случаи использования метода < String >.trim() , когда вызывающий код не выполняет никаких действий с возвращаемым значением метода.

В следующем примере аннотируется метод checkPermissions() для проверки того, действительно ли используется возвращаемое значение метода. Также предлагается метод enforcePermission() в качестве метода, который будет предложен разработчику в качестве замены:

Котлин

@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 для проверки того, что переопределяющий метод вызывает суперреализацию метода.

В следующем примере аннотируется метод onCreate() чтобы гарантировать, что любые переопределяющие реализации метода вызывают super.onCreate() :

Котлин

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

Ява

@CallSuper
protected void onCreate(Bundle savedInstanceState) {
}

Аннотации Typedef

Аннотации typedef проверяют, ссылается ли конкретный параметр, возвращаемое значение или поле на определённый набор констант. Они также позволяют автодополнению кода автоматически предлагать разрешённые константы.

Используйте аннотации @IntDef и @StringDef для создания перечисляемых аннотаций целочисленных и строковых наборов для проверки других типов ссылок на код.

Аннотации typedef используют @interface для объявления нового типа перечисляемой аннотации. Аннотации @IntDef и @StringDef , а также @Retention , аннотируют новую аннотацию и необходимы для определения перечисляемого типа. Аннотация @Retention(RetentionPolicy.SOURCE) указывает компилятору не сохранять данные перечисляемой аннотации в файле .class .

В следующем примере показаны шаги по созданию аннотации, которая проверяет, ссылается ли значение, переданное в качестве параметра метода, на одну из определенных констант:

Котлин

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

}

Ява

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 , чтобы проверить, ссылается ли параметр или возвращаемое значение на допустимый шаблон.

В следующем примере создается аннотация DisplayOptions со списком допустимых констант DISPLAY_ :

Котлин

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
...

Ява

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 всегда появляются в APK вашего приложения, даже если вы никогда не ссылаетесь на эти классы и методы в логике вашего приложения.

Чтобы сохранить небольшой размер приложения, подумайте, необходимо ли сохранять каждую аннотацию @Keep в нём. Если вы используете рефлексию для доступа к аннотированному классу или методу, используйте условие -if в правилах ProGuard, указав класс, который вызывает рефлексию.

Дополнительную информацию о том, как минимизировать код и указать, какой код не следует удалять, см . в статье Сжатие, обфускация и оптимизация вашего приложения .

Аннотации видимости кода

Используйте следующие аннотации для обозначения видимости определенных частей кода, таких как методы, классы, поля или пакеты.

Сделать код видимым для тестирования

Аннотация @VisibleForTesting указывает, что аннотированный метод более видим, чем обычно необходимо для его тестирования. Эта аннотация имеет необязательный аргумент otherwise , позволяющий указать, какой была бы видимость метода, если бы не было необходимости делать его видимым для тестирования. Lint использует аргумент otherwise для обеспечения требуемой видимости.

В следующем примере myMethod() обычно является private , но для тестов он package-private . С обозначением VisibleForTesting.PRIVATE lint выводит сообщение, если этот метод вызывается вне контекста, разрешенного для доступа в рамках private доступа, например, из другого модуля компиляции.

Котлин

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

Ява

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

Вы также можете указать @VisibleForTesting(otherwise = VisibleForTesting.NONE) чтобы указать, что метод предназначен только для тестирования. Эта форма аналогична @RestrictTo(TESTS) . Оба варианта выполняют одинаковую проверку линтинга.

Ограничить API

Аннотация @RestrictTo указывает, что доступ к аннотированному API (пакету, классу или методу) ограничен следующим образом:

Подклассы

Используйте форму аннотации @RestrictTo(RestrictTo.Scope.SUBCLASSES) , чтобы ограничить доступ API только подклассами.

Доступ к этому API имеют только классы, расширяющие аннотированный класс. Модификатор Java protected недостаточно строг, поскольку он разрешает доступ из не связанных классов в пределах того же пакета. Кроме того, бывают случаи, когда требуется оставить метод public для обеспечения гибкости в будущем, поскольку ранее protected и переопределённый метод невозможно сделать public , но при этом требуется указать, что класс предназначен для использования только внутри класса или из подклассов.

Библиотеки

Используйте форму аннотации @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) чтобы ограничить доступ API только вашими библиотеками.

Доступ к аннотированному API возможен только для вашего библиотечного кода. Это позволяет не только организовать код в любой желаемой иерархии пакетов, но и предоставить к нему общий доступ группе связанных библиотек. Эта возможность уже доступна для библиотек Jetpack, которые содержат большой объём кода реализации, не предназначенного для внешнего использования, но который должен быть public для использования различными дополнительными библиотеками Jetpack.

Тестирование

Используйте форму аннотации @RestrictTo(RestrictTo.Scope.TESTS) чтобы запретить другим разработчикам доступ к вашим API тестирования.

Доступ к аннотированному API возможен только для тестового кода. Это не позволит другим разработчикам использовать API, предназначенные исключительно для тестирования, для разработки.