refactor: Replace Injekt with Koin (Experiment) (#191)

* refactor: Use Koin

An experiment, aims to ditch Injekt and replace it with Koin while providing Injekt API facade for extensions

* fix: Mimic "InjektScope"

* fix: Mimic more classes

Completely fixed source search

* refactor(deps): Use Injekt-Koin library

* fix(r8): Keep Koin
This commit is contained in:
Ahmad Ansori Palembani 2024-09-19 13:18:02 +07:00 committed by GitHub
parent bc65f17f60
commit 9d858cc810
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 182 additions and 192 deletions

View file

@ -17,6 +17,7 @@
-keep,allowoptimization class com.google.gson.** { public protected *; }
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
-keep,allowoptimization class org.koin.** { public protected *; }
-keep,allowoptimization class eu.davidea.flexibleadapter.** { public protected *; }
-keep class io.requery.android.database.** { public protected *; }

View file

@ -59,13 +59,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.conscrypt.Conscrypt
import org.koin.core.context.startKoin
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.core.CrashlyticsLogWriter
import yokai.core.di.AppModule
import yokai.core.di.DomainModule
import yokai.core.di.PreferenceModule
import yokai.core.di.appModule
import yokai.core.di.domainModule
import yokai.core.di.preferenceModule
import yokai.core.migration.Migrator
import yokai.core.migration.migrations.migrations
import yokai.domain.base.BasePreferences
@ -97,10 +98,8 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
Injekt.apply {
importModule(PreferenceModule(this@App))
importModule(AppModule(this@App))
importModule(DomainModule())
startKoin {
modules(preferenceModule(this@App), appModule(this@App), domainModule())
}
basePreferences.crashReport().changes()

View file

@ -1,7 +1,6 @@
package yokai.core.di
import android.app.Application
import androidx.core.content.ContextCompat
import androidx.sqlite.db.SupportSQLiteOpenHelper
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
@ -27,23 +26,19 @@ import kotlinx.serialization.json.Json
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingleton
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
import org.koin.core.module.dsl.createdAtStart
import org.koin.core.module.dsl.withOptions
import org.koin.dsl.module
import yokai.data.AndroidDatabaseHandler
import yokai.data.Database
import yokai.data.DatabaseHandler
import yokai.domain.SplashState
import yokai.domain.storage.StorageManager
class AppModule(val app: Application) : InjektModule {
fun appModule(app: Application) = module {
single { app }
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
addSingletonFactory<SupportSQLiteOpenHelper> {
single<SupportSQLiteOpenHelper> {
val configuration = SupportSQLiteOpenHelper.Configuration.builder(app)
.callback(DbOpenCallback())
.name(DbOpenCallback.DATABASE_NAME)
@ -61,7 +56,7 @@ class AppModule(val app: Application) : InjektModule {
RequerySQLiteOpenHelperFactory().create(configuration)
}
addSingletonFactory<SqlDriver> {
single<SqlDriver> {
AndroidSqliteDriver(openHelper = get())
/*
AndroidSqliteDriver(
@ -78,20 +73,26 @@ class AppModule(val app: Application) : InjektModule {
)
*/
}
addSingletonFactory {
single {
Database(
driver = get(),
)
} withOptions {
createdAtStart()
}
addSingletonFactory<DatabaseHandler> { AndroidDatabaseHandler(get(), get()) }
addSingletonFactory { DatabaseHelper(app, get()) }
single<DatabaseHandler> { AndroidDatabaseHandler(get(), get()) }
addSingletonFactory { ChapterCache(app) }
single { DatabaseHelper(app, get()) } withOptions {
createdAtStart()
}
addSingletonFactory { CoverCache(app) }
single { ChapterCache(app) }
addSingletonFactory {
single { CoverCache(app) }
single {
NetworkHelper(
app,
get(),
@ -107,26 +108,34 @@ class AppModule(val app: Application) : InjektModule {
)
}
}
} withOptions {
createdAtStart()
}
addSingletonFactory { JavaScriptEngine(app) }
single { JavaScriptEngine(app) }
addSingletonFactory { SourceManager(app, get()) }
addSingletonFactory { ExtensionManager(app) }
single { SourceManager(app, get()) } withOptions {
createdAtStart()
}
single { ExtensionManager(app) }
addSingletonFactory { DownloadManager(app) }
single { DownloadManager(app) } withOptions {
createdAtStart()
}
addSingletonFactory { CustomMangaManager(app) }
single { CustomMangaManager(app) } withOptions {
createdAtStart()
}
addSingletonFactory { TrackManager(app) }
single { TrackManager(app) }
addSingletonFactory {
single {
Json {
ignoreUnknownKeys = true
explicitNulls = false
}
}
addSingletonFactory {
single {
XML {
defaultPolicy {
ignoreUnknownChildren()
@ -138,28 +147,12 @@ class AppModule(val app: Application) : InjektModule {
}
}
addSingletonFactory { ChapterFilter() }
single { ChapterFilter() }
addSingletonFactory { MangaShortcutManager() }
single { MangaShortcutManager() }
addSingletonFactory { AndroidStorageFolderProvider(app) }
addSingletonFactory { StorageManager(app, get()) }
single { AndroidStorageFolderProvider(app) }
single { StorageManager(app, get()) }
addSingletonFactory { SplashState() }
// Asynchronously init expensive components for a faster cold start
ContextCompat.getMainExecutor(app).execute {
get<NetworkHelper>()
get<SourceManager>()
get<Database>()
get<DatabaseHelper>()
get<DownloadManager>()
get<CustomMangaManager>()
}
}
single { SplashState() }
}

View file

@ -1,10 +1,6 @@
package yokai.core.di
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
import org.koin.dsl.module
import yokai.data.category.CategoryRepositoryImpl
import yokai.data.chapter.ChapterRepositoryImpl
import yokai.data.extension.repo.ExtensionRepoRepositoryImpl
@ -18,7 +14,6 @@ import yokai.domain.chapter.interactor.DeleteChapter
import yokai.domain.chapter.interactor.GetAvailableScanlators
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.InsertChapter
import yokai.domain.recents.interactor.GetRecents
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.extension.interactor.TrustExtension
import yokai.domain.extension.repo.ExtensionRepoRepository
@ -39,43 +34,42 @@ import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.recents.interactor.GetRecents
class DomainModule : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addFactory { TrustExtension(get(), get()) }
fun domainModule() = module {
factory { TrustExtension(get(), get()) }
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
addFactory { CreateExtensionRepo(get()) }
addFactory { DeleteExtensionRepo(get()) }
addFactory { GetExtensionRepo(get()) }
addFactory { GetExtensionRepoCount(get()) }
addFactory { ReplaceExtensionRepo(get()) }
addFactory { UpdateExtensionRepo(get(), get()) }
single<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
factory { CreateExtensionRepo(get()) }
factory { DeleteExtensionRepo(get()) }
factory { GetExtensionRepo(get()) }
factory { GetExtensionRepoCount(get()) }
factory { ReplaceExtensionRepo(get()) }
factory { UpdateExtensionRepo(get(), get()) }
addSingletonFactory<CustomMangaRepository> { CustomMangaRepositoryImpl(get()) }
addFactory { CreateCustomManga(get()) }
addFactory { DeleteCustomManga(get()) }
addFactory { GetCustomManga(get()) }
addFactory { RelinkCustomManga(get()) }
single<CustomMangaRepository> { CustomMangaRepositoryImpl(get()) }
factory { CreateCustomManga(get()) }
factory { DeleteCustomManga(get()) }
factory { GetCustomManga(get()) }
factory { RelinkCustomManga(get()) }
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
addFactory { GetManga(get()) }
addFactory { GetLibraryManga(get()) }
addFactory { InsertManga(get()) }
addFactory { UpdateManga(get()) }
single<MangaRepository> { MangaRepositoryImpl(get()) }
factory { GetManga(get()) }
factory { GetLibraryManga(get()) }
factory { InsertManga(get()) }
factory { UpdateManga(get()) }
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { DeleteChapter(get()) }
addFactory { GetAvailableScanlators(get()) }
addFactory { GetChapter(get()) }
addFactory { InsertChapter(get()) }
addFactory { UpdateChapter(get()) }
single<ChapterRepository> { ChapterRepositoryImpl(get()) }
factory { DeleteChapter(get()) }
factory { GetAvailableScanlators(get()) }
factory { GetChapter(get()) }
factory { InsertChapter(get()) }
factory { UpdateChapter(get()) }
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
single<HistoryRepository> { HistoryRepositoryImpl(get()) }
addFactory { GetRecents(get(), get()) }
factory { GetRecents(get(), get()) }
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
addFactory { GetCategories(get()) }
}
single<CategoryRepository> { CategoryRepositoryImpl(get()) }
factory { GetCategories(get()) }
}

View file

@ -9,10 +9,7 @@ import eu.kanade.tachiyomi.core.storage.AndroidStorageFolderProvider
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackPreferences
import eu.kanade.tachiyomi.network.NetworkPreferences
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
import org.koin.dsl.module
import yokai.domain.backup.BackupPreferences
import yokai.domain.base.BasePreferences
import yokai.domain.download.DownloadPreferences
@ -22,47 +19,45 @@ import yokai.domain.storage.StoragePreferences
import yokai.domain.ui.UiPreferences
import yokai.domain.ui.settings.ReaderPreferences
class PreferenceModule(val application: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingletonFactory<PreferenceStore> { AndroidPreferenceStore(application) }
fun preferenceModule(application: Application) = module {
single<PreferenceStore> { AndroidPreferenceStore(application) }
addSingletonFactory { BasePreferences(get()) }
single { BasePreferences(get()) }
addSingletonFactory { SourcePreferences(get()) }
single { SourcePreferences(get()) }
addSingletonFactory { TrackPreferences(get()) }
single { TrackPreferences(get()) }
addSingletonFactory { UiPreferences(get()) }
single { UiPreferences(get()) }
addSingletonFactory { ReaderPreferences(get()) }
single { ReaderPreferences(get()) }
addSingletonFactory { RecentsPreferences(get()) }
single { RecentsPreferences(get()) }
addSingletonFactory { DownloadPreferences(get()) }
single { DownloadPreferences(get()) }
addSingletonFactory {
single {
NetworkPreferences(
get(),
BuildConfig.FLAVOR == "dev" || BuildConfig.DEBUG || BuildConfig.NIGHTLY,
)
}
addSingletonFactory { SecurityPreferences(get()) }
single { SecurityPreferences(get()) }
addSingletonFactory { BackupPreferences(get()) }
single { BackupPreferences(get()) }
addSingletonFactory {
single {
PreferencesHelper(
context = application,
preferenceStore = get(),
)
}
addSingletonFactory {
single {
StoragePreferences(
folderProvider = get<AndroidStorageFolderProvider>(),
preferenceStore = get(),
)
}
}
}

View file

@ -33,7 +33,9 @@ kotlin {
androidMain {
dependencies {
// Dependency injection
api(libs.injekt.core)
api(project.dependencies.platform(libs.koin.bom))
api(libs.koin.core)
api(libs.koin.injekt)
// Network client
api(libs.okhttp)

View file

@ -12,6 +12,7 @@ sqlite = "2.4.0"
sqldelight = "2.0.2"
junit = "5.8.2"
kermit = "2.0.4"
koin = "4.0.0"
voyager = "1.1.0-beta02"
[libraries]
@ -41,10 +42,13 @@ flexbox = { module = "com.google.android.flexbox:flexbox", version = "3.0.0" }
flexible-adapter-ui = { module = "com.github.arkon.FlexibleAdapter:flexible-adapter-ui", version.ref = "flexible-adapter" }
flexible-adapter = { module = "com.github.arkon.FlexibleAdapter:flexible-adapter", version.ref = "flexible-adapter" }
image-decoder = { module = "com.github.tachiyomiorg:image-decoder", version = "41c059e540" }
injekt-core = { module = "com.github.null2264.injekt:injekt-core", version = "4135455a2a" }
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core" }
koin-injekt = { module = "com.github.null2264:injekt-koin", version = "7f944dd775" }
kotest-assertions = { module = "io.kotest:kotest-assertions-core", version = "5.9.1" }
libarchive = { module = "me.zhanghai.android.libarchive:library", version = "1.1.0" }

View file

@ -12,7 +12,9 @@ kotlin {
val commonMain by getting {
dependencies {
api(kotlinx.serialization.json)
api(libs.injekt.core)
api(project.dependencies.platform(libs.koin.bom))
api(libs.koin.core)
api(libs.koin.injekt)
api(libs.rxjava)
api(libs.jsoup)
}