From 9d858cc8109303abcaad70f6997c02155e04b9ab Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:18:02 +0700 Subject: [PATCH] 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 --- app/proguard-rules.pro | 1 + app/src/main/java/eu/kanade/tachiyomi/App.kt | 13 +- app/src/main/java/yokai/core/di/AppModule.kt | 215 +++++++++--------- .../main/java/yokai/core/di/DomainModule.kt | 68 +++--- .../java/yokai/core/di/PreferenceModule.kt | 63 +++-- core/build.gradle.kts | 4 +- gradle/libs.versions.toml | 6 +- source/api/build.gradle.kts | 4 +- 8 files changed, 182 insertions(+), 192 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 432fc49a4c..e82612fbc6 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 *; } diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 73d4e76cc5..1c3c1c1c34 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -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() diff --git a/app/src/main/java/yokai/core/di/AppModule.kt b/app/src/main/java/yokai/core/di/AppModule.kt index d1d3491452..c10a5b76a8 100644 --- a/app/src/main/java/yokai/core/di/AppModule.kt +++ b/app/src/main/java/yokai/core/di/AppModule.kt @@ -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,139 +26,133 @@ 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) + single { + val configuration = SupportSQLiteOpenHelper.Configuration.builder(app) + .callback(DbOpenCallback()) + .name(DbOpenCallback.DATABASE_NAME) + .noBackupDirectory(false) + .build() - addSingletonFactory { - val configuration = SupportSQLiteOpenHelper.Configuration.builder(app) - .callback(DbOpenCallback()) - .name(DbOpenCallback.DATABASE_NAME) - .noBackupDirectory(false) - .build() - - /* - if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Support database inspector in Android Studio - FrameworkSQLiteOpenHelperFactory().create(configuration) - } else { - RequerySQLiteOpenHelperFactory().create(configuration) - } - */ + /* + if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Support database inspector in Android Studio + FrameworkSQLiteOpenHelperFactory().create(configuration) + } else { RequerySQLiteOpenHelperFactory().create(configuration) } + */ + RequerySQLiteOpenHelperFactory().create(configuration) + } - addSingletonFactory { - AndroidSqliteDriver(openHelper = get()) - /* - AndroidSqliteDriver( - schema = Database.Schema, - context = app, - name = "tachiyomi.db", - factory = if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Support database inspector in Android Studio - FrameworkSQLiteOpenHelperFactory() - } else { - RequerySQLiteOpenHelperFactory() - }, - callback = get(), - ) - */ - } - addSingletonFactory { - Database( - driver = get(), - ) - } - addSingletonFactory { AndroidDatabaseHandler(get(), get()) } + single { + AndroidSqliteDriver(openHelper = get()) + /* + AndroidSqliteDriver( + schema = Database.Schema, + context = app, + name = "tachiyomi.db", + factory = if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Support database inspector in Android Studio + FrameworkSQLiteOpenHelperFactory() + } else { + RequerySQLiteOpenHelperFactory() + }, + callback = get(), + ) + */ + } - addSingletonFactory { DatabaseHelper(app, get()) } + single { + Database( + driver = get(), + ) + } withOptions { + createdAtStart() + } - addSingletonFactory { ChapterCache(app) } + single { AndroidDatabaseHandler(get(), get()) } - addSingletonFactory { CoverCache(app) } + single { DatabaseHelper(app, get()) } withOptions { + createdAtStart() + } - addSingletonFactory { - NetworkHelper( - app, - get(), - ) { builder -> - if (BuildConfig.DEBUG) { - builder.addInterceptor( - ChuckerInterceptor.Builder(app) - .collector(ChuckerCollector(app)) - .maxContentLength(250000L) - .redactHeaders(emptySet()) - .alwaysReadResponseBody(false) - .build(), - ) - } + single { ChapterCache(app) } + + single { CoverCache(app) } + + single { + NetworkHelper( + app, + get(), + ) { builder -> + if (BuildConfig.DEBUG) { + builder.addInterceptor( + ChuckerInterceptor.Builder(app) + .collector(ChuckerCollector(app)) + .maxContentLength(250000L) + .redactHeaders(emptySet()) + .alwaysReadResponseBody(false) + .build(), + ) } } + } 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 { - Json { - ignoreUnknownKeys = true - explicitNulls = false - } - } - addSingletonFactory { - XML { - defaultPolicy { - ignoreUnknownChildren() - } - autoPolymorphic = true - xmlDeclMode = XmlDeclMode.Charset - indent = 2 - xmlVersion = XmlVersion.XML10 - } - } - - addSingletonFactory { ChapterFilter() } - - addSingletonFactory { MangaShortcutManager() } - - addSingletonFactory { AndroidStorageFolderProvider(app) } - addSingletonFactory { StorageManager(app, get()) } - - addSingletonFactory { SplashState() } - - // Asynchronously init expensive components for a faster cold start - ContextCompat.getMainExecutor(app).execute { - get() - - get() - - get() - - get() - - get() - - get() + single { + Json { + ignoreUnknownKeys = true + explicitNulls = false } } + single { + XML { + defaultPolicy { + ignoreUnknownChildren() + } + autoPolymorphic = true + xmlDeclMode = XmlDeclMode.Charset + indent = 2 + xmlVersion = XmlVersion.XML10 + } + } + + single { ChapterFilter() } + + single { MangaShortcutManager() } + + single { AndroidStorageFolderProvider(app) } + single { StorageManager(app, get()) } + + single { SplashState() } } diff --git a/app/src/main/java/yokai/core/di/DomainModule.kt b/app/src/main/java/yokai/core/di/DomainModule.kt index cc9244af8a..7bd464e598 100644 --- a/app/src/main/java/yokai/core/di/DomainModule.kt +++ b/app/src/main/java/yokai/core/di/DomainModule.kt @@ -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 { ExtensionRepoRepositoryImpl(get()) } - addFactory { CreateExtensionRepo(get()) } - addFactory { DeleteExtensionRepo(get()) } - addFactory { GetExtensionRepo(get()) } - addFactory { GetExtensionRepoCount(get()) } - addFactory { ReplaceExtensionRepo(get()) } - addFactory { UpdateExtensionRepo(get(), get()) } + single { ExtensionRepoRepositoryImpl(get()) } + factory { CreateExtensionRepo(get()) } + factory { DeleteExtensionRepo(get()) } + factory { GetExtensionRepo(get()) } + factory { GetExtensionRepoCount(get()) } + factory { ReplaceExtensionRepo(get()) } + factory { UpdateExtensionRepo(get(), get()) } - addSingletonFactory { CustomMangaRepositoryImpl(get()) } - addFactory { CreateCustomManga(get()) } - addFactory { DeleteCustomManga(get()) } - addFactory { GetCustomManga(get()) } - addFactory { RelinkCustomManga(get()) } + single { CustomMangaRepositoryImpl(get()) } + factory { CreateCustomManga(get()) } + factory { DeleteCustomManga(get()) } + factory { GetCustomManga(get()) } + factory { RelinkCustomManga(get()) } - addSingletonFactory { MangaRepositoryImpl(get()) } - addFactory { GetManga(get()) } - addFactory { GetLibraryManga(get()) } - addFactory { InsertManga(get()) } - addFactory { UpdateManga(get()) } + single { MangaRepositoryImpl(get()) } + factory { GetManga(get()) } + factory { GetLibraryManga(get()) } + factory { InsertManga(get()) } + factory { UpdateManga(get()) } - addSingletonFactory { ChapterRepositoryImpl(get()) } - addFactory { DeleteChapter(get()) } - addFactory { GetAvailableScanlators(get()) } - addFactory { GetChapter(get()) } - addFactory { InsertChapter(get()) } - addFactory { UpdateChapter(get()) } + single { ChapterRepositoryImpl(get()) } + factory { DeleteChapter(get()) } + factory { GetAvailableScanlators(get()) } + factory { GetChapter(get()) } + factory { InsertChapter(get()) } + factory { UpdateChapter(get()) } - addSingletonFactory { HistoryRepositoryImpl(get()) } + single { HistoryRepositoryImpl(get()) } - addFactory { GetRecents(get(), get()) } + factory { GetRecents(get(), get()) } - addSingletonFactory { CategoryRepositoryImpl(get()) } - addFactory { GetCategories(get()) } - } + single { CategoryRepositoryImpl(get()) } + factory { GetCategories(get()) } } diff --git a/app/src/main/java/yokai/core/di/PreferenceModule.kt b/app/src/main/java/yokai/core/di/PreferenceModule.kt index 98a9b23086..ff49390d4c 100644 --- a/app/src/main/java/yokai/core/di/PreferenceModule.kt +++ b/app/src/main/java/yokai/core/di/PreferenceModule.kt @@ -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 { AndroidPreferenceStore(application) } +fun preferenceModule(application: Application) = module { + single { 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 { - NetworkPreferences( - get(), - BuildConfig.FLAVOR == "dev" || BuildConfig.DEBUG || BuildConfig.NIGHTLY, - ) - } + single { + NetworkPreferences( + get(), + BuildConfig.FLAVOR == "dev" || BuildConfig.DEBUG || BuildConfig.NIGHTLY, + ) + } - addSingletonFactory { SecurityPreferences(get()) } + single { SecurityPreferences(get()) } - addSingletonFactory { BackupPreferences(get()) } + single { BackupPreferences(get()) } - addSingletonFactory { - PreferencesHelper( - context = application, - preferenceStore = get(), - ) - } + single { + PreferencesHelper( + context = application, + preferenceStore = get(), + ) + } - addSingletonFactory { - StoragePreferences( - folderProvider = get(), - preferenceStore = get(), - ) - } + single { + StoragePreferences( + folderProvider = get(), + preferenceStore = get(), + ) } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index bdf8f3c21d..937662ecbe 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -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) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fee685be88..0eb31bdf59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/source/api/build.gradle.kts b/source/api/build.gradle.kts index f165cd33f4..8d6fe1986a 100644 --- a/source/api/build.gradle.kts +++ b/source/api/build.gradle.kts @@ -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) }