Skip to content

Latest commit

 

History

History
195 lines (136 loc) · 6.94 KB

File metadata and controls

195 lines (136 loc) · 6.94 KB

Разработка новых плагинов

Общая структура

Debug Panel построена на подходе с использованием плагинов — каждая функциональность реализуется в виде отдельного модуля-плагина.

Базовые модули, на которых основана работа панели:

  • panel-core — реализация панели, базовые классы системы плагинов и событийная модель.
  • panel-no-op — пустые реализации публичных API для релизных сборок (исключает отладочный код из продакшена).

Создание нового плагина

1. Создать модуль

Создайте новый модуль в директории plugins/:

plugins/
└── plugin-your-feature/

2. Зарегистрировать модуль в settings.gradle.kts

Добавьте модуль по аналогии с существующими плагинами:

// Plugins
include(
    ":plugins:plugin-your-feature",
)

3. Настроить build.gradle.kts

Примените convention-плагин, который содержит всю необходимую конфигурацию (compileSdk, minSdk, explicitApi(), зависимость на panel-core и Compose):

plugins {
    id("convention.debug.panel.plugin")
}

description = "Plugin description"

android {
    namespace = "com.redmadrobot.debug.plugin.yourfeature"
}

dependencies {
    // Только специфичные для плагина зависимости
}

4. Создать класс плагина

Класс плагина — точка входа, отвечающая за взаимодействие с DebugPanel. Унаследуйтесь от Plugin() и реализуйте обязательные методы. Подробнее о getPluginContainer() и PluginDependencyContainer — в разделе ниже.

public class YourPlugin(
    /* аргументы для инициализации */
) : Plugin() {

    override fun getName(): String = "YOUR PLUGIN"

    override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer {
        return YourPluginContainer(commonContainer)
    }

    @Composable
    override fun content() {
        YourScreen()
    }
}

Если плагин поддерживает редактирование через экран настроек панели, реализуйте интерфейс EditablePlugin:

public class YourPlugin : Plugin(), EditablePlugin {
    // ...

    @Composable
    override fun content() {
        YourScreen(isEditMode = false)
    }

    @Composable
    override fun settingsContent() {
        YourScreen(isEditMode = true)
    }
}

5. Создать UI на Jetpack Compose

UI плагина реализуется с помощью Composable-функций. Для инъекции ViewModel используется хелпер provideViewModel:

@Composable
internal fun YourScreen(
    viewModel: YourViewModel = provideViewModel {
        getPlugin<YourPlugin>()
            .getContainer<YourPluginContainer>()
            .createYourViewModel()
    },
) {
    val state by viewModel.state.collectAsState()
    // UI
}

PluginDependencyContainer

В библиотеке не используются DI-фреймворки, чтобы не добавлять лишних зависимостей. Вместо этого применяется подход Service Locator.

Для этого создайте класс-контейнер, реализующий интерфейс PluginDependencyContainer, и инициализируйте в нём необходимые зависимости. Context доступен через CommonContainer, который передаётся в метод getPluginContainer() при инициализации плагина.

internal class YourPluginContainer(
    private val container: CommonContainer,
) : PluginDependencyContainer {

    private val dataStore by lazy { YourDataStore(container.context) }

    val repository by lazy { YourRepository(dataStore) }

    fun createYourViewModel(): YourViewModel {
        return YourViewModel(repository)
    }
}

Пример реализации: ServersPluginContainer

Работа с классом плагина

Класс плагина является точкой доступа к данным и зависимостям. Для получения экземпляра плагина используйте getPlugin<YourPlugin>():

getPlugin<YourPlugin>()
    .getContainer<YourPluginContainer>()

Области видимости

Все модули используют explicitApi() — модификаторы видимости обязательны для всех объявлений. Внутренние классы, не предназначенные для использования в клиентском приложении, должны иметь модификатор internal.

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

Для тестирования плагина:

  1. Подключите его как зависимость в модуль sample:
debugImplementation(project(":plugins:plugin-your-feature"))
  1. Инициализируйте плагин в классе App sample-приложения:
DebugPanel.initialize(
    application = this,
    plugins = listOf(
        YourPlugin(/* ... */)
    )
)
  1. Запустите sample-проект.

No-op реализации

Чтобы отладочный код не попадал в релизную сборку, для каждого плагина необходимо создать no-op реализацию в модуле panel-no-op.

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

Важно: package должен совпадать с оригинальным пакетом вашего модуля.

В sample-приложении подключение выглядит так:

debugImplementation(project(":plugins:plugin-your-feature"))
releaseImplementation(project(":panel-no-op"))

Подробнее о подходе: No-op versions for dev tools

Публикация

Публикация новых плагинов проходит через создание Pull Request в ветку main.