теги:
koin предоставляет простой в использовании интерфейс API для Android , позволяющий легко получить доступ к инфраструктуре koin.
[градусная конфигурация коина в Android]
1. startKoin в классе Application
Из своего класса вы можете использовать функцию и внедрить контекст Android следующим образом:
Application startKoin androidContext class MainApplication : Application()
override fun onCreate() super.onCreate() startKoin // Log Koin into Android logger androidLogger() // Reference Android context androidContext(this@MainApplication) // Load modules modules(myAppModules)
Если вам нужно запустить Koin из другого класса Android, вы можете использовать эту функцию, чтобы предоставить свой экземпляр Android следующим образом:startKoin
Context
startKoin
//inject Android context
androidContext(/* your android context */)
// ...
2. Дополнительная настройка
Из вашей конфигурации Koin (в блочном коде) вы также можете настроить различные части Koin.startKoin
2.1 Логирование монет для Android
koin предоставляет реализацию log.
startKoin
// use Android logger - Level.INFO by default
androidLogger()
// ...
2.2 Свойства нагрузки
Вы можете использовать свойства Koin для хранения ключей/значений в файле: assets/koin.properties
startKoin
// ...
// use properties from assets/koin.properties
androidFileProperties()
3. Внедрить экземпляр объекта в Android
3.1 Подготовка к классу Android
koin предоставляет KoinComponents
расширения, доступные для компонентов Android, в том числеActivity
Fragment Service ComponentCallbacks
Вы можете получить доступ к расширениям Kotlin следующим образом:
by inject()
- Ленивый вычислительный экземпляр из контейнера Koin
get()
- Получить экземпляр из контейнера Koin
Мы можем объявить свойство для ленивой инъекции:
module
// definition of Presenter
factory Presenter()
класс DetailActivity : AppCompatActivity()
// Lazy inject Presenter
override val presenter : Presenter by inject()
override fun onCreate(savedInstanceState: Bundle?)
//...
Или мы можем получить экземпляр напрямую:
override fun onCreate(savedInstanceState: Bundle?) super.onCreate(savedInstanceState)
// Retrieve a Presenter instance val presenter : Presenter = get()
Примечание. Если ваш класс не расширен, просто добавьте интерфейс KoinComponent, если вам нужно, или экземпляр из другого класса. ввести() получить()
3.2 Использование контекста Android
class MainApplication : Application()
override fun onCreate() super.onCreate() startKoin //inject Android context androidContext(this@MainApplication) // ...
В вашем определении следующая функция позволяет вам получать экземпляры в модуле Koin, чтобы помочь вам легко писать выражения, требующие экземпляров.androidContext() androidApplication() Context Application
val appModule = module
// create a Presenter instance with injection of R.string.mystring resources from Android factory MyPresenter(androidContext().resources.getString(R.string.mystring))
4. Конструкторы DSL для Android
4.1 Конструкторы DSL
Koin теперь предоставляет новое ключевое слово DSL, которое позволяет вам напрямую нацеливаться на конструкторы классов и избегать ввода ваших определений в лямбда-выражениях.
Для Android это означает следующие новые ключевые слова конструктора DSL:
viewModelOf()
- эквивалентноviewModel
fragmentOf()
- эквивалентноfragment
workerOf()
- эквивалентноworker
Примечание. Обязательно используйте перед именем класса, чтобы найти конструктор класса::
4.2 Примеры функций Android DSL
Дано приложение Android со следующими компонентами:
// A simple service
class SimpleServiceImpl() : SimpleService
// Presenter, использующий SimpleService, может получить «id», введенный в
класс параметра FactoryPresenter (valid id: String, val service: SimpleService)
// ViewModel, которая может получить введенный параметр «id», использовать SimpleService и получить
класс SavedStateHandle SimpleViewModel (val id: String, val service: SimpleService, val handle: SavedStateHandle) : ViewModel()
// Сессия с заданной областью действия, которая может получить ссылку на
класс MyActivity (из области видимости) Session(val activity: MyActivity)
// Worker, использующий SimpleService и получающий
класс Context и WorkerParameters SimpleWorker(
private val simpleService: SimpleService,
appContext: Context,
private val params: WorkerParameters
) : CoroutineWorker(appContext, params)
Мы можем объявить их так:
module singleOf(::SimpleServiceImpl) bind<SimpleService>()
factoryOf(::FactoryPresenter) viewModelOf(::SimpleViewModel) scope<MyActivity>() scopedOf(::Session) workerOf(::SimpleWorker)
5. Мультимодульное использование koin в Android
Используя Koin, вы можете описывать определения в модулях. В этом разделе мы увидим, как объявлять, организовывать и связывать модули.
5.1 коин мультимодуль
Компоненты не обязательно должны находиться в одном модуле. Модули — это логические пространства, которые помогают вам организовать определения и могут зависеть от других модулей определений. Определения ленивы и затем разрешаются только тогда, когда компонент запрашивает их.
Возьмем пример, когда связанные компоненты находятся в отдельных модулях:
// ComponentB <- ComponentA
class ComponentA()
class ComponentB(val componentA : ComponentA)
val moduleA = модуль
// Singleton ComponentA
single ComponentA()
val moduleB = модуль
// Singleton ComponentB со связанным экземпляром ComponentA
single ComponentB(get())
Нам просто нужно объявить список используемых модулей при запуске контейнера Koin:
class MainApplication : Application()
override fun onCreate() super.onCreate() startKoin // ... // Load modules modules(moduleA, moduleB)
5.2 Модуль содержит
В классе доступна новая функция, которая позволяет комбинировать модули, включая другие модули в организованном и структурированном виде.includes() Module
Новый модуль имеет 2 выдающиеся особенности:
Разделите большие модули на более мелкие, более целенаправленные модули.
В модульных проектах это позволяет более точно контролировать видимость модуля (см. пример ниже).
Как это работает? Возьмем несколько модулей, включаем модули в: parentModule
// `:feature` module
val childModule1 = module
/* Other definitions here. */
val childModule2 = модуль
/* Другие определения здесь. */
val parentModule = модуль
включает (childModule1, childModule2)
// :app
модуль
startKoin modules(parentModule)
Обратите внимание, что нам не нужно явно устанавливать все модули: при включении все объявленные модули будут загружены автоматически.
parentModule includes childModule1 childModule2 parentModule childModule1 childModule2
ИНФОРМАЦИЯ: загрузка модулей теперь оптимизирована, чтобы сгладить все графики модулей и избежать дублирования определений модулей.
Наконец, вы можете включить несколько вложенных или повторяющихся модулей, и Koin объединит все включенные модули, удалив дубликаты:
// :feature module
val dataModule = module
/* Other definitions here. */
val domainModule = модуль
/* Другие определения здесь. */
val featureModule1 = модуль
включает (domainModule, dataModule)
val featureModule2 = модуль
включает (domainModule, dataModule)
// :
класс модуля приложения MainApplication : Application()
override fun onCreate()
super.onCreate()
startKoin
// ...
// Load modules
modules(featureModule1, featureModule2)
Обратите внимание, что все модули будут включены только один раз:dataModule domainModule featureModule1 featureModule2
5.3 Android ViewModel и навигация
Модуль Gradle представляет новое ключевое слово DSL, которое помогает объявить и связать компоненты ViewModel с жизненным циклом компонентов Android. Также доступно ключевое слово, позволяющее объявить ViewModel с помощью его конструктора.koin-android viewModel singlefactory viewModelOf
val appModule = module
// ViewModel for Detail View viewModel DetailViewModel(get(), get()) // or directly with constructor viewModelOf(::DetailViewModel)
Объявленный компонент должен расширять как минимум класс. Вы можете указать, как внедрить конструктор класса и использовать эту функцию для внедрения зависимостей.android.arch.lifecycle.ViewModel get()
Примечание. Ключевое слово помогает объявить фабричный экземпляр ViewModel. Этот экземпляр будет обрабатываться внутренним ViewModelFactory и при необходимости повторно подключать экземпляр ViewModel. Это также позволит вводить параметры.viewModel viewModelOf
5.4 Внедрение ViewModel
Используйте viewModel в компоненте Android,Activity Fragment Service
by viewModel()
- Ленивые свойства делегата для внедрения моделей представления в свойства
getViewModel()
- Получить экземпляр модели представления напрямую
class DetailActivity : AppCompatActivity()
// Lazy inject ViewModel val detailViewModel: DetailViewModel by viewModel()
5.5 Общая ViewModel активности
Экземпляр ViewModel может совместно использоваться фрагментом и его основным действием.
Чтобы внедрить общую модель представления при использовании: Фрагмент
by activityViewModel()
- Ленивые делегированные свойства для внедрения общих экземпляров viewModel в свойства.
get ActivityViewModel()
- Получить общий экземпляр viewModel напрямую
Просто объявите модель представления один раз:
val weatherAppModule = module
// WeatherViewModel declaration for Weather View components viewModel WeatherViewModel(get(), get())
Примечание: квалификаторы viewModel будут рассматриваться как теги viewModel.
и повторно используйте его в Activity и Fragment:
class WeatherActivity : AppCompatActivity()
/* * Declare WeatherViewModel with Koin and allow constructor dependency injection */ private val weatherViewModel by viewModel<WeatherViewModel>()
класс WeatherHeaderFragment: Фрагмент()
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by activityViewModel<WeatherViewModel>()
класс WeatherListFragment: Фрагмент()
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by activityViewModel<WeatherViewModel>()
5.6 Передача аргументов конструкторам
Передайте параметры в viewModel, пример кода выглядит следующим образом:
в модуле
val appModule = module
// ViewModel for Detail View with id as parameter injection viewModel parameters -> DetailViewModel(id = parameters.get(), get(), get()) // ViewModel for Detail View with id as parameter injection, resolved from graph viewModel DetailViewModel(get(), get(), get()) // or Constructor DSL viewModelOf(::DetailViewModel)
Входящие параметры точки внедрения зависимостей
class DetailActivity : AppCompatActivity()
val id : String // id of the view // Lazy inject ViewModel with id parameter val detailViewModel: DetailViewModel by viewModel parametersOf(id)
5.7 Внедрение SavedStateHandle
Добавьте новые свойства, введенные в конструктор, для обработки состояния ViewModel:SavedStateHandle
class MyStateVM(val handle: SavedStateHandle, val myService : MyService) : ViewModel()
В модуле Koin просто проанализируйте его с параметром или: get()
viewModel MyStateVM(get(), get())
или используйте конструктор DSL:
viewModelOf(::MyStateVM)
Фрагмент активности
by viewModel()
- Ленивые свойства делегата для внедрения экземпляров модели представления состояния в свойства.
getViewModel()
- Получить экземпляр модели представления состояния напрямую
class DetailActivity : AppCompatActivity()
// MyStateVM viewModel injected with SavedStateHandle val myStateVM: MyStateVM by viewModel()
5.8 ViewModel в навигационной навигационной диаграмме
Вы можете ограничить экземпляры ViewModel графом навигации. Просто передайте идентификатор, чтобыby koinNavGraphViewModel()
class NavFragment : Fragment()
val mainViewModel: NavViewModel by koinNavGraphViewModel(R.id.my_graph)
5.9 Общий API viewModel
Koin предоставляет некоторые «низкоуровневые» API для непосредственной настройки экземпляра ViewModel.viewModelForClass ComponentActivity Fragment
ComponentActivity.viewModelForClass(
clazz: KClass<T>,
qualifier: Qualifier? = null,
owner: ViewModelStoreOwner = this,
state: BundleDefinition? = null,
key: String? = null,
parameters: ParametersDefinition? = null,
): Lazy<T>
Также предусмотрена функция верхнего уровня:
fun <T : ViewModel> getLazyViewModelForClass(
clazz: KClass<T>,
owner: ViewModelStoreOwner,
scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
qualifier: Qualifier? = null,
state: BundleDefinition? = null,
key: String? = null,
parameters: ParametersDefinition? = null,
): Lazy<T>
5.10 ViewModel API — совместимость с Java
В зависимости необходимо добавить совместимость с Java:
// Java Compatibility
implementation "io.insert-koin:koin-android-compat:$koin_version"
您可以使用以下函数或静态函数将 ViewModel 实例注入到 Java 代码库中:viewModel() getViewModel() ViewModelCompat
@JvmOverloads
@JvmStatic
@MainThread
fun <T : ViewModel> getViewModel(
владелец: ViewModelStoreOwner,
clazz: Class<T>,
квалификатор: Qualifier? = null,
параметры: ParametersDefinition? = null
)
6. Внедрить в Jetpack Compose
Сначала узнайте о Jetpack Compose:
developer.android.com/jetpack/com…
6.1 Внедрение @Composable
При написании составных функций вы можете получить доступ к следующим API-интерфейсам Koin:
get()
- Получить экземпляр из контейнера Koin
getKoin()
- Получить текущий экземпляр Koin
Для модуля, объявляющего компонент «MyService»:
val androidModule = module
single MyService()
Мы можем получить ваш экземпляр следующим образом:
@Composable
fun App()
val myService = get<MyService>()
Примечание. Для обеспечения единообразия функциональности Jetpack Compose лучше всего вводить экземпляр непосредственно в атрибут функции. Этот подход позволяет использовать Koin для реализации по умолчанию, но оставляет его открытым для внедрения экземпляров по мере необходимости.
@Composable
fun App(myService: MyService = get())
6.2 ViewModel @Composable
Точно так же, как вы получаете доступ к классическому экземпляру singleton/factory, вы можете получить доступ к следующим API-интерфейсам Koin ViewModel:
getViewModel()
или - получить экземплярkoinViewModel()
Для модуля, объявляющего компонент "MyViewModel":
module
viewModel MyViewModel()
// or constructor DSL
viewModelOf(::MyViewModel)
Мы можем получить ваш экземпляр следующим образом:
@Composable
fun App()
val vm = koinViewModel<MyViewModel>()
Мы можем получить ваш экземпляр в параметре функции:
@Composable
fun App(vm : MyViewModel = koinViewModel())
7. Управление областями Android
Компоненты Android, например Activity、Fragment、Service
, имеют жизненный цикл, эти компоненты создаются Системой, и в компонентах есть соответствующие обратные вызовы жизненного цикла.
Поскольку у компонентов Android есть атрибуты жизненного цикла, экземпляры компонентов нельзя передавать в koin. По продолжительности жизненного цикла компоненты можно разделить на три категории:
- • Долгоживущий компонент (
Service、database
) — используется несколькими экранами, никогда не выбрасывается. - • Компоненты среднего периода (
User session
) — используются несколькими экранами и должны быть удалены через определенный период времени. - • Компоненты с коротким сроком службы (
ViewModel)
--используются только одним Экраном и должны быть удалены в конце Экрана
Для долгосрочных компонентов мы обычно используем single для создания единого экземпляра глобально в приложении.
В режиме архитектуры MVP Presenter является компонентом с коротким циклом.
Способ создания его в Activity выглядит следующим образом
class DetailActivity : AppCompatActivity()
// injected Presenter override val presenter : Presenter by inject()
Мы также можем создать в модуле
Мы используем область фабрики для создания экземпляра Presenter.
val androidModule = module
// Factory instance of Presenter factory Presenter()
Создайте область экземпляра, привязанную к области
val androidModule = module
scope<DetailActivity> scoped Presenter()
Большинство утечек памяти Android происходят из-за ссылок на компоненты пользовательского интерфейса/Android из компонентов, отличных от Android. Система хранит ссылки на него и не может полностью восстановить его с помощью сборки мусора.
7.1 Объявление области Android
Чтобы квалифицировать зависимость от компонента Android, вы должны объявить область действия с помощью следующего блока:
class MyPresenter()
class MyAdapter(val presenter : MyPresenter)
модуль
// Объявить область действия для MyActivity
scope<MyActivity>
// получить экземпляр MyPresenter из текущей области
видимости MyAdapter(get())
scoped MyPresenter()
7.2 Класс Android Scope
Koin предоставляет классы Scope, связанные с компонентами жизненного цикла Android.ScopeActivity Retained ScopeActivity ScopeFragment
class MyActivity : ScopeActivity()
// MyPresenter is resolved from MyActivity's scope val presenter : MyPresenter by inject()
Android Scope необходимо использовать с интерфейсами для реализации таких полей:AndroidScopeComponent scope
abstract class ScopeActivity( @LayoutRes contentLayoutId: Int = 0, ) : AppCompatActivity(contentLayoutId), AndroidScopeComponent
override val scope: Scope by activityScope() override fun onCreate(savedInstanceState: Bundle?) super.onCreate(savedInstanceState) checkNotNull(scope)
Нам нужно использовать интерфейсы и реализовывать свойства. Это установит область действия по умолчанию, используемую классом.AndroidScopeComponent scope
7.3 Интерфейс Android Scope
Чтобы создать область действия Koin, привязанную к компоненту Android, просто используйте следующую функцию:
createActivityScope()
- Создайте Scope для текущего Activity (часть Scope должна быть объявлена)
createActivityRetainedScope()
- Создайте RetainedScope (поддерживается жизненным циклом ViewModel) для текущего действия (часть Scope должна быть объявлена)
createFragmentScope()
- Создайте область для текущего фрагмента и ссылку на родительскую область действия. Эти функции можно использовать в качестве делегатов для реализации различных типов областей:
activityScope()
- Создайте Scope для текущего Activity (часть Scope должна быть объявлена)
activityRetainedScope()
- Создайте RetainedScope (поддерживается жизненным циклом ViewModel) для текущего действия (часть Scope должна быть объявлена)
fragmentScope()
- Создайте область для текущего фрагмента и свяжите ее с родительской областью действия.
class MyActivity() : AppCompatActivity(contentLayoutId), AndroidScopeComponent
override val scope: Scope by activityScope()
Мы также можем установить область хранения (на основе жизненного цикла ViewModel), используя следующее:
class MyActivity() : AppCompatActivity(contentLayoutId), AndroidScopeComponent
override val scope: Scope by activityRetainedScope()
Если вы не хотите использовать класс Android Scope, вы можете использовать свой собственный класс и создать API с Scope.AndroidScopeComponent
7.4 Ссылка на объем
Ссылки областей позволяют совместно использовать экземпляры между компонентами с настраиваемыми областями. В более широком смысле вы можете использовать экземпляры в компонентах. Например, если нам нужно поделиться экземпляром.Scope UserSession
Сначала объявите определение области действия:
module
// Shared user session data
scope(named("session"))
scoped UserSession()
Когда пришло время начать использовать экземпляр, создайте для него область действия:UserSession
val ourSession = getKoin().createScope("ourSession",named("session"))
// связываем область видимости ourSession с текущей scope
из ScopeActivity или ScopeFragment
scope.linkTo(ourSession)
Затем используйте его везде, где вам нужно:
class MyActivity1 : ScopeActivity()
fun reuseSession() val ourSession = getKoin().createScope("ourSession",named("session")) // link ourSession scope to current `scope`, from ScopeActivity or ScopeFragment scope.linkTo(ourSession) // will look at MyActivity1's Scope + ourSession scope to resolve val userSession = get<UserSession>()
класс MyActivity2: ScopeActivity()
fun reuseSession()
val ourSession = getKoin().createScope("ourSession",named("session"))
// link ourSession scope to current `scope`, from ScopeActivity or ScopeFragment
scope.linkTo(ourSession)
// will look at MyActivity2's Scope + ourSession scope to resolve
val userSession = get<UserSession>()
8. Фабрика фрагментов
Поскольку AndroidX выпустил серию пакетов для расширения функциональности Androidandroidx.fragment Fragment
developer.android.com/jetpack/и…
8.1 Фабрика фрагментов
С момента выпуска был представлен класс, предназначенный для создания экземпляров класса: 2.1.0-alpha-3 FragmentFactory
Фрагмент.
developer.android.com/reference/k…
Koin также предоставляет фабричный класс KoinFragmentFactory
Fragment для создания Fragment.
8.2 Настройка фабрики фрагментов
Во-первых, в KoinApplication
объявлении установите экземпляр по умолчанию, используя ключевое слово: fragmentFactory()
KoinFragmentFactory .
startKoin // setup a KoinFragmentFactory instance fragmentFactory()
modules(...)
8.3 Объявить и внедрить фрагмент
Объявить фрагмент и внедрить его в модуль
class MyFragment(val myService: MyService) : Fragment()
val appModule = модуль
, один фрагмент MyService()
MyFragment(get())
8.4 Получить фрагмент
использовать setupKoinFragmentFactory()
настройкиFragmentFactory
Чтобы запросить свой фрагмент, используйтеsupportFragmentManager
supportFragmentManager.beginTransaction()
.replace<MyFragment>(R.id.mvvm_frame)
.commit()
Добавьте необязательные параметры
supportFragmentManager.beginTransaction()
.replace<MyFragment>(
containerViewId = R.id.mvvm_frame,
args = MyBundle(),
tag = MyString()
)
8.5 Фабрика фрагментов и прицелы монет
Если вы хотите использовать Koin Activity Scope, вы должны объявить свой фрагмент как определение в своей области действия:scoped
val appModule = module
scope<MyActivity>
fragment MyFragment(get())
И настройте свою фабрику фрагментов монет с вашим прицелом:setupKoinFragmentFactory(lifecycleScope)
class MyActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?) // Koin Fragment Factory setupKoinFragmentFactory(lifecycleScope) super.onCreate(savedInstanceState) //...
9. Инъекция коинов WorkManager
koin предоставляет отдельный пакет компонентов koin-androidx-workmanager для WorkManager.
Во-первых, в объявлении KoinApplication используйте ключевое слово для установки пользовательского экземпляра WorkManager:workManagerFactory()
class MainApplication : Application(), KoinComponent
override fun onCreate() super.onCreate() startKoin // setup a WorkManager instance workManagerFactory() modules(...) setupWorkManagerFactory()
Модификация AndroidManifest.xml, чтобы избежать использования значения по умолчанию
<application . . .>
. . .
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="$applicationId.workmanager-init"
tools:node="remove" />
</application>
9.1 Объявление ListenableWorker
val appModule = module
single MyService()
worker MyListenableWorker(get())
9.2 Создайте дополнительную фабрику WorkManagerFactory
class MainApplication : Application(), KoinComponent
override fun onCreate() super.onCreate() startKoin workManagerFactory(workFactory1, workFactory2) . . . setupWorkManagerFactory()
Если и Koin, и workFactory1 WorkManagerFactory
могут быть созданы ListenableWorker
, будет использована предоставленная Koin фабрика.
9.3 Изменение манифеста самой koin lib
Если koin-androidx-workmanager
фабрика по умолчанию отключена, и разработчик приложения не инициализирует инфраструктуру диспетчера работ koin, у него не будет доступной фабрики диспетчера работ.
В связи с описанной выше ситуацией, мы делаем следующие улучшения DSL:
val workerFactoryModule = module
factory<WorkFactory> WorkFactory1()
factory<WorkFactory> WorkFactory2()
Затем внутри коина сделайте что-то вроде
fun Application.setupWorkManagerFactory(
// no vararg for WorkerFactory
)
. . .
getKoin().getAll<WorkerFactory>()
.forEach
delegatingWorkerFactory.addFactory(it)
справочная ссылка
рекомендуемое чтение
- Платформа внедрения зависимостей Koin of kotlin (1)
- Платформа внедрения зависимостей Koin of kotlin (2)
- Инфраструктура внедрения зависимостей Koin of kotlin (3)
Автор: Calvin873
Ссылка: https://juejin.cn/post/7189917106580750395