Разработка
Написание символьного процессора с помощью Kotlin Symbol Processing (Часть 1)
В этом уроке вы создадите символьный процессор, который генерирует фабричный класс для Фрагмента.
Kotlin Symbol Processing (KSP) позволяет добавить возможность генерации кода в приложение. KSP использует аннотации для создания легких плагинов компилятора. С его помощью можно генерировать шаблонный код или добавлять в ваш код мощные функциональные возможности.
Эта тема также требует базовых знаний об аннотациях. Если вы не знакомы с этой темой, прочитайте учебник Annotations: Supercharge Your Development.
В этом уроке вы создадите символьный процессор, который генерирует фабричный класс для Фрагмента. Фабричный класс позволяет передавать данные фрагменту через Bundle во время инициализации.
Вы также узнаете другие подробности:
- Конфигурирование аннотации
- Как KSP воспринимает ваш код
- Передача аргументов процессору и логирование следов
Начало работы
Скачайте учебный проект в руководстве на сайте Kodeco, а затем откройте стартовый проект в Android Studio.
Структура проекта
В нем вы увидите три модуля:
- Annotation: Здесь хранится ваш класс аннотаций
- Processor: Содержит логику генерации кода KSP
- App: Это приложение для Android, которое использует сгенерированные файлы
Эти модули имеют заранее настроенные зависимости. Общая структура зависимостей показана ниже.
Как модуль :app, так и модуль :processor включают в себя модуль :annotation. Кроме того, модуль :app также зависит от модуля :processor.
Соберите и запустите проект.
На рисунке выше показан ShuffleFragment
. При нажатии на кнопку выбирается случайный покемон.
Это DetailFragment
, который показывает подробную информацию о покемоне. Случайный покемон передается в DetailFragment
через Bundle
. Наша цель — заменить функцию createDetailFragment
в DetailFragment
на сгенерированную.
Прежде чем приступать к обновлению проекта, полезно понять, что такое KSP и генерация кода.
Изучим способы генерации кода
Генерировать код в исходниках Kotlin можно тремя способами:
KAPT
Kotlin Annotation Processing Tool — или kapt — это решение для генерации кода, которое заставляет annotationProcessor
в Java работать с файлами на Java и Kotlin. Хотя на него легко перейти, он опирается на извлечение из исходных файлов Kotlin сущностей Java, которые процессор может понять. Это делает его более медленным для файлов Kotlin.
Плагин компилятора Kotlin
Плагины компилятора Kotlin — это модули, которые имеют доступ к низкоуровневым API компилятора Kotlin. Чаще всего они используются для генерации кода, но могут также модифицировать существующий байткод и предоставлять более богатые функциональные возможности существующему коду.
Хорошим примером является плагин Parcelize, который генерирует реализации Parcelable для классов данных. Такой подход имеет ряд недостатков, в частности, API компилятора часто меняются, что затрудняет их обслуживание. Кроме того, эти API компилятора не очень хорошо документированы, поэтому работать с ними становится сложнее.
KSP
KSP пытается преодолеть разрыв между написанием плагинов компилятора и удобством сопровождения. Считайте, что это слой, защищающий ваш генератор кода от изменений API компилятора. Это также означает, что некоторая функциональность подключаемых модулей компилятора будет недоступна. KSP является Kotlin First, что означает, что он распознает синтаксис Kotlin. Это делает его более быстрым, поскольку он не полагается на извлечение сущностей Java.
Теперь, когда вы знаете больше о KSP, давайте начнем с обновления проекта с помощью аннотаций.
Знакомство с аннотациями
Аннотации — это точки входа в исходный код. Большинство инструментов генерации кода опираются на них. KSP работает на том же фундаменте.
Определение собственной аннотации
Начнем с определения класса аннотации. Он служит для поиска фрагментов, которым нужна фабрика. Перейдите в модуль :annotation вашего проекта и добавьте новый файл FragmentFactory.kt.
Затем добавьте к нему декларацию аннотации:
package com.yourcompany.fragmentfactory.annotation import kotlin.annotation.AnnotationRetention.SOURCE import kotlin.reflect.KClass @Target(AnnotationTarget.CLASS) //1 @Retention(SOURCE) //2 annotation class FragmentFactory(val type: KClass<*>, val parcelKey: String)//3
Давайте рассмотрим ее пошагово:
- Это объявление указывает на то, что ваша аннотация должна использоваться для классов. Поскольку вас интересуют только Фрагменты, это будет правильным выбором. Другие варианты — для классов
PROPERTY
иFUNCTION
. - Значение
SOURCE
для возврата означает, что вы хотите, чтобы FragmentFactory была доступна только во время компиляции, а не внутри вашего APK. - Параметр
type
указывает тип класса объекта, который необходимо обработать. ПараметрparcelKey
— это ключ, который будет использоваться для хранения сериализованных данных в Bundle.
Аннотирование фрагмента
Откройте DetailFragment и добавьте приведенное ниже содержимое прямо над объявлением фрагмента.
import com.yourcompany.fragmentfactory.DetailFragment.Companion.KEY_POKEMON import com.yourcompany.fragmentfactory.annotation.FragmentFactory @FragmentFactory(Pokemon::class,KEY_POKEMON)
Таким образом, DetailFragment становится доступным для вашего процессора через аннотацию. Теперь вы готовы написать процессор, который читает эту аннотацию.
Обращение к Символьным процессорам
KSP обращается к процессорам символов на этапе компиляции. Вся логика фильтрации токенов KSP и генерации кода происходит в них.
Добавление процессора
Начнем с перехода к пакету com.yourcompany.fragmentfactory.processor
в модуле :processor проекта. Создайте в нем файл FragmentFactoryProcessor.kt.
После этого добавьте в него декларации процессоров.
package com.yourcompany.fragmentfactory.processor import com.google.devtools.ksp.processing.CodeGenerator import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.symbol.KSAnnotated class FragmentFactoryProcessor( private val logger: KSPLogger, codeGenerator: CodeGenerator ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { logger.info("FragmentFactoryProcessor was invoked.") return emptyList() } }
Ваш символьный процессор будет расширять класс SymbolProcessor и реализовывать метод process
. Обработка символов в Kotlin происходит в несколько проходов. В каждом метод process()
может возвращать список символов, которые недоступны или будут обработаны в будущих раундах. Это называется отложенной обработкой и позволяет нескольким процессорам хорошо взаимодействовать друг с другом, когда один из них зависит от результатов работы другого.
Определение провайдера
В KSP Provider — это просто фабрика вашего processor. Как правило, здесь вы возвращаете экземпляр своего процессора. Добавьте новый файл FragmentFactoryProcessorProvider.kt в существующий пакет com.yourcompany.fragmentfactory.processor
. Он также будет находиться в модуле :processor.
Затем добавьте в него следующий код:
package com.yourcompany.fragmentfactory.processor import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.processing.SymbolProcessorProvider class FragmentFactoryProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { return FragmentFactoryProcessor( logger = environment.logger, codeGenerator = environment.codeGenerator ) } }
Функция create
вызывается всякий раз, когда KSP необходимо создать экземпляр вашего SymbolProcessor. Это дает доступ к environment
, который предоставляет дефолтный логер. CodeGenerator
предоставляет методы для создания и управления файлами. Более того, только созданные из него файлы доступны KSP для инкрементной обработки и компиляции.
Регистрация провайдера
Последним шагом в настройке процессора является регистрация его провайдера. Это делается путем определения его квалифицированной ссылки в специальном файле в каталоге src/main/resources/META-INF/services
. Перейдите в эту папку, как показано ниже.
Создайте файл с именем com.google.devtools.ksp.processing.SymbolProcessorProvider
с содержимым, приведенным ниже:
com.yourcompany.fragmentfactory.processor.FragmentFactoryProcessorProvider
KSP будет использовать эту ссылку для поиска вашего провайдера. Это похоже на то, как Android определяет путь к вашей Активити, читая файл AndroidManifest.xml.
Далее перейдите в файл app/build.gradle и добавьте следующую строку в раздел зависимостей:
ksp(project(":processor"))
Это позволит процессору обрабатывать исходные файлы модуля :app.
Ура, вы только что сконфигурировали свой первый SymbolProcessor. Теперь откройте вкладку Terminal в Android Studio и выполните следующую команду:
./gradlew clean installDebug --info
Вы должны увидеть результат сборки, аналогичный приведенному ниже:
Вы также увидите каталог ksp в папке build вашего модуля :app.
Продолжение: Написание символьного процессора с помощью Kotlin Symbol Processing (Часть 2)
-
Интегрированные среды разработки2 недели назад
Лучшая работа с Android Studio: 5 советов
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2024.43
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2024.44
-
Исследования2 недели назад
Поможет ли новая архитектура React Native отобрать лидерство у Flutter в кроссплатформенной разработке?