Skip to content

Commit c94528b

Browse files
committed
docs: plugin dev docs revision
1 parent 652a5f6 commit c94528b

File tree

2 files changed

+137
-124
lines changed

2 files changed

+137
-124
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
# Debug-panel
22

3-
Библиотека для отладки приложений.
3+
Библиотека предоставляет встроенную отладочную панель для Android-приложений, предназначенную для использования в debug и QA-сборках.
44

55
<img width="1925" height="1090" alt="debug_preview" src="https://github.com/user-attachments/assets/52d83dcb-a8e6-4c86-9d65-87b27d4796bf" />
66

77
[![Maven Central Version](https://img.shields.io/maven-central/v/com.redmadrobot.debug/panel-core?style=flat-square)](https://central.sonatype.com/search?namespace=com.redmadrobot.debug)
88
[![License](https://img.shields.io/github/license/RedMadRobot/debug-panel-android?style=flat-square)][license]
99
[![Android](https://img.shields.io/badge/Android-3DDC84?style=flat-square&logo=android&logoColor=white)](#)
1010

11-
**[Changelog][changelog]** | **[Миграция на новые версии][migration-guide]**
11+
**[Changelog][changelog]** | **[Миграция на новые версии][migration-guide]** | **[Документация по разработке плагинов][plugin-development-doc]**
1212

13-
Библиотека избавляет от необходимости пересобирать приложение для смены сервера или переключения feature toggle. Цель проекта — упростить процесс отладки приложения.
13+
Панель позволяет управлять состоянием приложения в runtime без внесения изменений в основной код.
1414

1515
Основные возможности:
1616

17-
1. **Добавление, редактирование и выбор сервера.**
18-
2. **Управление feature-toggles и remote config на основе Konfeature.**
19-
3. **Отображение информации о приложении.**
17+
1. Добавление, редактирование и выбор сервера.
18+
2. Управление feature-toggles и remote config на основе Konfeature.
19+
3. Отображение информации о приложении.
2020

21-
Библиотека построена на подходе с использованием плагинов: каждая функциональность подключается отдельным модулем в зависимостях.
21+
Каждая функциональность подключается отдельным плагином.
2222

2323
## Подключение библиотеки
2424

@@ -126,7 +126,7 @@ DebugPanel.subscribeToEvents(lifecycleOwner = this) { event ->
126126
}
127127
```
128128

129-
Получение выбранного или сервера по умолчанию:
129+
Получение выбранного сервера или сервера по умолчанию:
130130

131131
```kotlin
132132
val selectedServer = ServersPlugin.getSelectedServer()

docs/plugin_development.md

Lines changed: 129 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,182 +1,195 @@
11
# Разработка новых плагинов
22

3-
**[!]Важно.**
4-
1. Библиотека находится в статусе разработки и миграции на более актуальные решения.
5-
Актуальность данного документа стоит уточнить у холдера библиотеки. В данный момент это **r.choryev@redmadrobot.com**
6-
2. В библиотеке млогут быть спорные решения, но она открыта для предложений.
7-
3. В текущих плагинах, для работы со списками используется [Groupie](https://github.com/lisawray/groupie).
8-
Т.к. эта библиотека требует использование `jcenter` и уже не кажется таким уж подходящим решением, поэтому она будет удаляться из библиотеки.
9-
Поэтому это стоит учесть при разработке ваших новых плагинов и использовать какое-то другое решение.
3+
## Общая структура
104

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

12-
## Общая структура
13-
Debug panel разрабатывается опираясь на подход разработки функционала отдельными плагинами.
14-
В данный момент есть несколько модулей на которых основана работа самой панели и разработка и работа плагинов.
7+
Базовые модули, на которых основана работа панели:
158

16-
* **debug-panel-core** - Реализация самой панели и базовых классов для поддержки системы плагинов.
17-
* **debug-panel-common** - Модуль содержащий общие библиотеки, классы и ресурсы переиспользуемые в плагинах.
18-
Библиотеки пробрасываются как сквозные зависимости при помощи типа зависимости `api`.
19-
Список предоставляемых библиотек можно посмотреть в файле [build.gradle](../debug-panel-common/build.gradle.kts)
9+
* **panel-core** — реализация панели, базовые классы системы плагинов и событийная модель.
10+
* **panel-no-op** — пустые реализации публичных API для релизных сборок (исключает отладочный код из продакшена).
2011

2112
## Создание нового плагина
22-
Для добавления нового плагина необходимо сделать несколько шагов:
23-
1. Создать в дирректории **plugins** новый модуль для реализации своего плагина.
24-
25-
```
26-
plugins
27-
| your-plugin
28-
```
29-
2. Объявить новый модуль в файле **settings.gradle** по примеру уже существующих плагинов.
30-
31-
```
32-
include ':your-plugin'
3313

34-
project(':your-plugin').projectDir = new File(rootDir, 'plugins/your-plugin')
14+
### 1. Создать модуль
15+
16+
Создайте новый модуль в директории `plugins/`:
17+
18+
```
19+
plugins/
20+
└── plugin-your-feature/
3521
```
36-
3. Добавить в **build.gradle** файл вашего модуля следующие настройки:
3722

38-
```groovy
39-
android {
40-
/*.......*/
41-
42-
compileSdkVersion build_versions.compile_sdk
23+
### 2. Зарегистрировать модуль в settings.gradle.kts
4324

44-
defaultConfig {
45-
minSdkVersion build_versions.min_sdk
46-
targetSdkVersion build_versions.target_sdk
25+
Добавьте модуль по аналогии с существующими плагинами:
4726

48-
versionCode getVersionCodeFromProperties()
49-
versionName getVersionNameFromProperties()
50-
}
27+
```kotlin
28+
// Plugins
29+
include(
30+
":plugins:plugin-your-feature",
31+
)
32+
```
5133

52-
/*.......*/
34+
### 3. Настроить build.gradle.kts
5335

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

55-
kotlinOptions {
56-
freeCompilerArgs += "-Xexplicit-api=strict"
57-
}
38+
```kotlin
39+
plugins {
40+
id("convention.debug.panel.plugin")
5841
}
5942

60-
dependencies {
61-
implementation(
62-
project(path: ':debug-panel-core'),
63-
project(path: ':debug-panel-common'),
43+
description = "Plugin description"
6444

65-
deps.kotlin.stdlib
66-
)
45+
android {
46+
namespace = "com.redmadrobot.debug.plugin.yourfeature"
6747
}
6848

49+
dependencies {
50+
// Только специфичные для плагина зависимости
51+
}
6952
```
70-
**[!]Важно. Конфигурация будет меняться при дальнейшей миграции с Groovy на Kotlin**
7153

72-
4. Создать в своем модуле класс-плагин который и будет отвечать за взаимодействие с DebugPanel.
73-
Для этого класс должен унаследоваться от класса `Plugin()` и реализовать необходимые методы.
74-
В качестве аргументов класса можно передать необходимые для инициализации плагина данные.\
75-
**(О методе `getPluginContainer()` и `PluginDependencyContainer()` можно будет почитать ниже)**
76-
54+
### 4. Создать класс плагина
55+
56+
Класс плагина — точка входа, отвечающая за взаимодействие с DebugPanel.
57+
Унаследуйтесь от `Plugin()` и реализуйте обязательные методы.
58+
Подробнее о `getPluginContainer()` и `PluginDependencyContainer` — в разделе ниже.
59+
7760
```kotlin
7861
public class YourPlugin(
79-
/*some arguments*/
62+
/* аргументы для инициализации */
8063
) : Plugin() {
8164

82-
internal companion object {
83-
const val NAME = "AWESOME PLUGIN"
84-
}
85-
86-
override fun getName(): String = NAME
65+
override fun getName(): String = "YOUR PLUGIN"
8766

88-
/*Plugin dependency container initializing*/
8967
override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer {
90-
return YourPluginContainer(sharedPreferences)
68+
return YourPluginContainer(commonContainer)
9169
}
9270

93-
/*Plugin Fragment initializing*/
94-
override fun getFragment(): Fragment? {
95-
return YourPluginFragment()
71+
@Composable
72+
override fun content() {
73+
YourScreen()
74+
}
75+
}
76+
```
77+
78+
Если плагин поддерживает редактирование через экран настроек панели, реализуйте интерфейс `EditablePlugin`:
79+
80+
```kotlin
81+
public class YourPlugin : Plugin(), EditablePlugin {
82+
// ...
83+
84+
@Composable
85+
override fun content() {
86+
YourScreen(isEditMode = false)
9687
}
9788

98-
/*Plugin Setting Fragment initializing.*/
99-
override fun getSettingFragment(): Fragment { //Нужно только если есть отдельный экран для настройки плагина.
100-
return YourPluginSettingFragment()
89+
@Composable
90+
override fun settingsContent() {
91+
YourScreen(isEditMode = true)
10192
}
10293
}
10394
```
104-
5. Создать **Fragment** экрана плагина(если он нужен) и унаследовать его от `PluginFragment()`.
105-
К фрагменту создать **ViewModel** и унаследовать от `PluginViewModel()`.
106-
В этой связке (Fragment+ViewModel), реализовывать пользовательское взаимодействие пользователя с плагином.
107-
108-
## PluginDependencyContainer
10995

110-
В библиотеке не используются библиотеки для реализации DI, т.к.:
111-
1. Не хочется тащить их зависимости в библиотеку.
112-
2. Библиотека не такая большая чтобы реализовывать полноценный DI.
96+
### 5. Создать UI на Jetpack Compose
11397

114-
Вместо этого, в библиотеке используется подход с **Service Locator**.
115-
Для этого необходимо создать свой класс-контейнер, унаследовать его от **PluginDependencyContainer** и внутри него инициировать необходимые зависимости.
116-
Если вам для этого понадобится **Context**, его можно получить из **CommonContainer** который прилетает в качестве аргумента в методе `getPluginContainer()` при инициализации плагина.
117-
Пример реализации можно [посмотреть тут](../plugins/accounts-plugin/src/main/kotlin/com/redmadrobot/account/plugin/AccountsPluginContainer.kt)
98+
UI плагина реализуется с помощью Composable-функций. Для инъекции ViewModel используется хелпер `provideViewModel`:
11899

119-
## Работа с классом плагина
100+
```kotlin
101+
@Composable
102+
internal fun YourScreen(
103+
viewModel: YourViewModel = provideViewModel {
104+
getPlugin<YourPlugin>()
105+
.getContainer<YourPluginContainer>()
106+
.createYourViewModel()
107+
},
108+
) {
109+
val state by viewModel.state.collectAsState()
110+
// UI
111+
}
112+
```
113+
114+
## PluginDependencyContainer
115+
116+
В библиотеке не используются DI-фреймворки, чтобы не добавлять лишних зависимостей. Вместо этого применяется подход **Service Locator**.
120117

121-
Класс-плагин, описание которого было в пункте **4**, является точкой инициализации вашего плагина.
122-
Поэтому доступ к данным и различным экземплярам классов нужно реализовывать через него.
123-
Чтобу получить доступ к самому плагину, нужно использовать метод `getPlugin<YourPlugin>()`.
124-
Например для получения контейнера зависимостей плагина, нужно вызвать:
118+
Для этого создайте класс-контейнер, реализующий интерфейс `PluginDependencyContainer`, и инициализируйте в нём необходимые зависимости.
119+
`Context` доступен через `CommonContainer`, который передаётся в метод `getPluginContainer()` при инициализации плагина.
125120

126121
```kotlin
127-
getPlugin<YourPlugin>()
128-
.getContainer<YourPluginContainer>()
122+
internal class YourPluginContainer(
123+
private val container: CommonContainer,
124+
) : PluginDependencyContainer {
125+
126+
private val dataStore by lazy { YourDataStore(container.context) }
127+
128+
val repository by lazy { YourRepository(dataStore) }
129+
130+
fun createYourViewModel(): YourViewModel {
131+
return YourViewModel(repository)
132+
}
133+
}
129134
```
130135

131-
**Пример использования плагина для получения ViewModel во Fragment:**
136+
Пример реализации: [ServersPluginContainer](../plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt)
137+
138+
## Работа с классом плагина
139+
140+
Класс плагина является точкой доступа к данным и зависимостям.
141+
Для получения экземпляра плагина используйте `getPlugin<YourPlugin>()`:
132142

133143
```kotlin
134-
private val viewModel by lazy {
135-
obtainShareViewModel {
136-
getPlugin<ServersPlugin>()
137-
.getContainer<ServersPluginContainer>()
138-
.createServersViewModel()
139-
}
140-
}
144+
getPlugin<YourPlugin>()
145+
.getContainer<YourPluginContainer>()
141146
```
142147

143148
## Области видимости
144149

145-
Все внутренние классы используемые только для работы плагина и не требующиеся для работы клиентского приложения, должны иметь область видимости **inner**
150+
Все модули используют `explicitApi()` — модификаторы видимости обязательны для всех объявлений.
151+
Внутренние классы, не предназначенные для использования в клиентском приложении, должны иметь модификатор `internal`.
146152

147153
## Тестирование
148154

149-
Для тестирования плагина, необходимо:
150-
1. Подключить его как зависимость в модуль `sample`.
155+
Для тестирования плагина:
156+
157+
1. Подключите его как зависимость в модуль `sample`:
151158

152-
```groovy
153-
debugImplementation(project(path: ':your-plugin'))
159+
```kotlin
160+
debugImplementation(project(":plugins:plugin-your-feature"))
154161
```
155162

156-
2. Инициировать плагин в **App** классе **sample** приложения.
163+
2. Инициализируйте плагин в классе `App` sample-приложения:
157164

158165
```kotlin
159-
DebugPanel.initialize(
160-
application = this,
161-
plugins = listOf(YourPluggin())
166+
DebugPanel.initialize(
167+
application = this,
168+
plugins = listOf(
169+
YourPlugin(/* ... */)
170+
)
162171
)
163172
```
164173

165-
3. Запустить **sample** проект
174+
3. Запустите sample-проект.
166175

167-
## No-op зависимости
176+
## No-op реализации
168177

169-
Для того чтобы в релизную сборку не попадали реализации публичных классов вашего модуля, необходимо добавить их в модуль no-op зависимостей.
170-
([Подробнее в статье](https://medium.com/@orhanobut/no-op-versions-for-dev-tools-b0a865934398)). \
171-
Для этого создайте пакет с именем вашего плагина в модуле **debug-panel-no-op** и скопируйте ваши публичные классы доступные пользователю в этот пакет.
178+
Чтобы отладочный код не попадал в релизную сборку, для каждого плагина необходимо создать no-op реализацию в модуле **panel-no-op**.
172179

173-
[!]Важно. Поле **package** должно остаться оригинальным.
174-
Таким, каким оно было в вашем модуле.
180+
Создайте пакет с публичными классами плагина, доступными пользователю, и предоставьте пустые реализации.
175181

176-
## Публикация
182+
Важно: **package** должен совпадать с оригинальным пакетом вашего модуля.
183+
184+
В sample-приложении подключение выглядит так:
177185

178-
Публикация новых плагнинов в основном репозитории должна проходить через создание **Merge Request** в ветку **develop**.
186+
```kotlin
187+
debugImplementation(project(":plugins:plugin-your-feature"))
188+
releaseImplementation(project(":panel-no-op"))
189+
```
190+
191+
Подробнее о подходе: [No-op versions for dev tools](https://medium.com/@orhanobut/no-op-versions-for-dev-tools-b0a865934398)
192+
193+
## Публикация
179194

180-
Публикация на внутренний Maven пока делается вручную.
181-
За публикацией обращаться к r.choryev@redmadrobot.com.
182-
В ближайшее время есть планы пересмотреть этот подход.
195+
Публикация новых плагинов проходит через создание **Pull Request** в ветку **main**.

0 commit comments

Comments
 (0)