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