Revert "Use app scoped CoroutineScope (#3403)"

This reverts commit 509eee5dfb.

Fixes #3429
This commit is contained in:
AntsyLich
2026-06-18 17:31:52 +06:00
parent ee6a9876ec
commit 80541831bb
17 changed files with 96 additions and 87 deletions
@@ -19,13 +19,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeoutOrNull
@@ -63,16 +63,17 @@ import kotlin.time.Duration.Companion.seconds
*/
class DownloadCache(
private val context: Context,
private val scope: CoroutineScope,
private val provider: DownloadProvider = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(),
private val storageManager: StorageManager = Injekt.get(),
) {
private val scope = CoroutineScope(Dispatchers.IO)
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
val changes = _changes.receiveAsFlow()
.onStart { emit(Unit) }
.flowOn(Dispatchers.IO)
.shareIn(scope, SharingStarted.Lazily, 1)
/**
@@ -100,7 +101,7 @@ class DownloadCache(
init {
// Attempt to read cache file
scope.launchIO {
scope.launch {
rootDownloadsDirMutex.withLock {
try {
if (diskCacheFile.exists()) {
@@ -4,7 +4,6 @@ import android.content.Context
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.Page
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.drop
@@ -36,7 +35,6 @@ import uy.kohesive.injekt.api.get
*/
class DownloadManager(
private val context: Context,
private val scope: CoroutineScope,
private val provider: DownloadProvider = Injekt.get(),
private val cache: DownloadCache = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
@@ -47,7 +45,7 @@ class DownloadManager(
/**
* Downloader whose only task is to download chapters.
*/
private val downloader = Downloader(context, provider, cache, scope)
private val downloader = Downloader(context, provider, cache)
val isRunning: Boolean
get() = downloader.isRunning
@@ -223,7 +221,7 @@ class DownloadManager(
* @param source the source of the chapters.
*/
fun deleteChapters(chapters: List<Chapter>, manga: Manga, source: Source) {
scope.launchIO {
launchIO {
val filteredChapters = getChaptersToDelete(chapters, manga)
if (filteredChapters.isEmpty()) {
return@launchIO
@@ -250,7 +248,7 @@ class DownloadManager(
* @param removeQueued whether to also remove queued downloads.
*/
fun deleteManga(manga: Manga, source: Source, removeQueued: Boolean = true) {
scope.launchIO {
launchIO {
if (removeQueued) {
downloader.removeFromQueue(manga)
}
@@ -18,6 +18,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,6 +44,7 @@ import okhttp3.Response
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.extension
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchNow
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat
@@ -70,7 +72,6 @@ class Downloader(
private val context: Context,
private val provider: DownloadProvider,
private val cache: DownloadCache,
private val scope: CoroutineScope,
private val sourceManager: SourceManager = Injekt.get(),
private val chapterCache: ChapterCache = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(),
@@ -95,6 +96,7 @@ class Downloader(
*/
private val notifier by lazy { DownloadNotifier(context) }
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private var downloaderJob: Job? = null
/**
@@ -110,7 +112,7 @@ class Downloader(
var isPaused: Boolean = false
init {
scope.launch {
launchNow {
val chapters = async { store.restore() }
addAllToQueue(chapters.await())
}
@@ -189,7 +191,7 @@ class Downloader(
private fun launchDownloaderJob() {
if (isRunning) return
downloaderJob = scope.launchIO {
downloaderJob = scope.launch {
val activeDownloadsFlow = combine(
queueState,
downloadPreferences.parallelSourceLimit.changes(),
@@ -28,11 +28,10 @@ import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notify
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import tachiyomi.core.common.Constants
import tachiyomi.core.common.i18n.pluralStringResource
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.lang.launchUI
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.Manga
@@ -45,7 +44,6 @@ import java.text.NumberFormat
class LibraryUpdateNotifier(
private val context: Context,
private val scope: CoroutineScope = Injekt.get(),
private val securityPreferences: SecurityPreferences = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
) {
@@ -210,7 +208,7 @@ class LibraryUpdateNotifier(
// Per-manga notification
if (!securityPreferences.hideNotificationContent.get()) {
scope.launch {
launchUI {
context.notify(
updates.map { (manga, chapters) ->
NotificationManagerCompat.NotificationWithIdAndTag(
@@ -17,12 +17,9 @@ import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.Chapter
@@ -35,8 +32,6 @@ import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
/**
@@ -50,7 +45,6 @@ class NotificationReceiver : BroadcastReceiver() {
private val getChapter: GetChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private val downloadManager: DownloadManager by injectLazy()
private val scope: CoroutineScope by injectLazy()
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
@@ -203,7 +197,7 @@ class NotificationReceiver : BroadcastReceiver() {
val downloadPreferences: DownloadPreferences = Injekt.get()
val sourceManager: SourceManager = Injekt.get()
async(scope, Dispatchers.IO) {
launchIO {
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
.map {
val chapter = it.copy(read = true)
@@ -229,27 +223,13 @@ class NotificationReceiver : BroadcastReceiver() {
* @param mangaId id of manga
*/
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
async(scope, Dispatchers.IO) {
val manga = getManga.await(mangaId) ?: return@async
launchIO {
val manga = getManga.await(mangaId) ?: return@launchIO
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
downloadManager.downloadChapters(manga, chapters)
}
}
private fun BroadcastReceiver.async(
scope: CoroutineScope,
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit,
) {
val result = goAsync()
try {
scope.launch(context, start, block)
} finally {
result.finish()
}
}
companion object {
private const val NAME = "NotificationReceiver"
@@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.di
import android.app.Application
import androidx.core.content.ContextCompat
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import app.cash.sqldelight.db.SqlDriver
import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteConfiguration
@@ -22,9 +20,6 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.JavaScriptEngine
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.AndroidSourceManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.plus
import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import nl.adaptivity.xmlutil.XmlDeclMode
@@ -112,20 +107,18 @@ class AppModule(val app: Application) : InjektModule {
ProtoBuf
}
addSingletonFactory<CoroutineScope> { ProcessLifecycleOwner.get().lifecycleScope + SupervisorJob() }
addSingletonFactory { ChapterCache(app, get()) }
addSingletonFactory { CoverCache(app) }
addSingletonFactory { NetworkHelper(app, get(), get()) }
addSingletonFactory { NetworkHelper(app, get()) }
addSingletonFactory { JavaScriptEngine(app) }
addSingletonFactory<SourceManager> { AndroidSourceManager(app, get(), get(), get()) }
addSingletonFactory { ExtensionManager(app, get()) }
addSingletonFactory<SourceManager> { AndroidSourceManager(app, get(), get()) }
addSingletonFactory { ExtensionManager(app) }
addSingletonFactory { DownloadProvider(app) }
addSingletonFactory { DownloadManager(app, get()) }
addSingletonFactory { DownloadCache(app, get()) }
addSingletonFactory { DownloadManager(app) }
addSingletonFactory { DownloadCache(app) }
addSingletonFactory { TrackerManager() }
addSingletonFactory { DelayedTrackingStore(app) }
@@ -135,7 +128,7 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { AndroidStorageFolderProvider(app) }
addSingletonFactory { LocalSourceFileSystem(get()) }
addSingletonFactory { LocalCoverManager(app, get()) }
addSingletonFactory { StorageManager(app, get(), get()) }
addSingletonFactory { StorageManager(app, get()) }
// Asynchronously init expensive components for a faster cold start
ContextCompat.getMainExecutor(app).execute {
@@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -40,10 +41,12 @@ import java.util.Locale
*/
class ExtensionManager(
private val context: Context,
private val scope: CoroutineScope,
private val preferences: SourcePreferences = Injekt.get(),
private val trustExtension: TrustExtension = Injekt.get(),
) {
val scope = CoroutineScope(SupervisorJob())
private val _isInitialized = MutableStateFlow(false)
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
@@ -55,7 +58,7 @@ class ExtensionManager(
/**
* The installer which installs, updates and uninstalls the extensions.
*/
private val installer by lazy { ExtensionInstaller(context, scope) }
private val installer by lazy { ExtensionInstaller(context) }
private val iconMap = mutableMapOf<String, Drawable>()
@@ -12,15 +12,16 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.isPackageInstalled
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.launch
import logcat.LogPriority
import okhttp3.OkHttpClient
import okhttp3.Request
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.system.logcat
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -33,8 +34,9 @@ import java.io.File
*/
internal class ExtensionInstaller(
private val context: Context,
private val scope: CoroutineScope,
) {
private val scope = CoroutineScope(Dispatchers.IO)
private val activeJobs = mutableMapOf<String, Job>()
private val activeSteps = mutableMapOf<Long, MutableStateFlow<InstallStep>>()
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller
@@ -55,7 +57,7 @@ internal class ExtensionInstaller(
val step = MutableStateFlow(InstallStep.Pending)
activeSteps[downloadId] = step
val job = scope.launchIO {
val job = scope.launch {
val tmpFile = File(context.cacheDir, "extension_${extension.pkgName}.apk")
try {
step.value = InstallStep.Downloading
@@ -5,14 +5,16 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.repository.StubSourceRepository
import tachiyomi.domain.source.service.SourceManager
@@ -24,7 +26,6 @@ import java.util.concurrent.ConcurrentHashMap
class AndroidSourceManager(
private val context: Context,
private val scope: CoroutineScope,
private val extensionManager: ExtensionManager,
private val sourceRepository: StubSourceRepository,
) : SourceManager {
@@ -34,6 +35,8 @@ class AndroidSourceManager(
private val downloadManager: DownloadManager by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.IO)
private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap<Long, Source>())
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
@@ -41,7 +44,7 @@ class AndroidSourceManager(
override val sources: Flow<List<Source>> = sourcesMapFlow.map { it.values.toList() }
init {
scope.launchIO {
scope.launch {
extensionManager.installedExtensionsFlow
.collectLatest { extensions ->
val mutableMap = ConcurrentHashMap<Long, Source>(
@@ -64,7 +67,7 @@ class AndroidSourceManager(
}
}
scope.launchIO {
scope.launch {
sourceRepository.subscribeAll()
.collectLatest { sources ->
val mutableMap = stubSourcesMap.toMutableMap()
@@ -95,9 +98,9 @@ class AndroidSourceManager(
}
private fun registerStubSource(source: StubSource) {
scope.launchIO {
scope.launch {
val dbSource = sourceRepository.getStubSource(source.id)
if (dbSource == source) return@launchIO
if (dbSource == source) return@launch
sourceRepository.upsertStubSource(source.id, source.lang, source.name)
if (dbSource != null) {
downloadManager.renameSource(dbSource, source)
@@ -286,7 +286,7 @@ class ReaderViewModel @JvmOverloads constructor(
val context = Injekt.get<Application>()
val source = sourceManager.getOrStub(manga.source)
loader = ChapterLoader(context, viewModelScope, downloadManager, downloadProvider, manga, source)
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
Result.success(true)
@@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import kotlinx.coroutines.CoroutineScope
import mihon.core.archive.archiveReader
import mihon.core.archive.epubReader
import tachiyomi.core.common.i18n.stringResource
@@ -23,7 +22,6 @@ import tachiyomi.source.local.io.Format
*/
class ChapterLoader(
private val context: Context,
private val scope: CoroutineScope,
private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider,
private val manga: Manga,
@@ -102,7 +100,7 @@ class ChapterLoader(
is Format.Epub -> EpubPageLoader(format.file.epubReader(context))
}
}
source is HttpSource -> HttpPageLoader(chapter, source, scope)
source is HttpSource -> HttpPageLoader(chapter, source)
source is StubSource -> error(context.stringResource(MR.strings.source_not_installed, source.toString()))
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
}
@@ -8,8 +8,9 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runInterruptible
@@ -30,10 +31,11 @@ import kotlin.math.min
internal class HttpPageLoader(
private val chapter: ReaderChapter,
private val source: HttpSource,
scope: CoroutineScope,
private val chapterCache: ChapterCache = Injekt.get(),
) : PageLoader() {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
/**
* A queue used to manage requests one by one while allowing priorities.
*/
@@ -122,14 +124,14 @@ internal class HttpPageLoader(
queue.offer(PriorityPage(page, PriorityPage.RETRY))
}
@OptIn(DelicateCoroutinesApi::class)
override fun recycle() {
super.recycle()
scope.cancel()
queue.clear()
// Cache current page list progress for online chapters to allow a faster reopen
chapter.pages?.let { pages ->
GlobalScope.launchIO {
launchIO {
try {
// Convert to pages without reader information
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
@@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
import eu.kanade.tachiyomi.network.interceptor.IgnoreGzipInterceptor
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import kotlinx.coroutines.CoroutineScope
import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.brotli.BrotliInterceptor
@@ -16,7 +15,6 @@ import java.util.concurrent.TimeUnit
class NetworkHelper(
private val context: Context,
private val preferences: NetworkPreferences,
scope: CoroutineScope,
) {
val cookieJar = AndroidCookieJar()
@@ -64,7 +62,7 @@ class NetworkHelper(
val client = clientBuilder
.addInterceptor(
CloudflareInterceptor(context, cookieJar, scope, ::defaultUserAgentProvider),
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider),
)
.build()
@@ -11,7 +11,6 @@ import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.network.AndroidCookieJar
import eu.kanade.tachiyomi.util.system.isOutdated
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import okhttp3.Cookie
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
@@ -26,9 +25,8 @@ import java.util.concurrent.CountDownLatch
class CloudflareInterceptor(
private val context: Context,
private val cookieManager: AndroidCookieJar,
scope: CoroutineScope,
defaultUserAgentProvider: () -> String,
) : WebViewInterceptor(context, scope, defaultUserAgentProvider) {
) : WebViewInterceptor(context, defaultUserAgentProvider) {
private val executor = ContextCompat.getMainExecutor(context)
@@ -9,12 +9,11 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.setDefaultSettings
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import tachiyomi.core.common.util.lang.launchUI
import tachiyomi.i18n.MR
import java.util.Locale
import java.util.concurrent.CountDownLatch
@@ -22,7 +21,6 @@ import java.util.concurrent.TimeUnit
abstract class WebViewInterceptor(
private val context: Context,
private val scope: CoroutineScope,
private val defaultUserAgentProvider: () -> String,
) : Interceptor {
@@ -58,7 +56,7 @@ abstract class WebViewInterceptor(
}
if (!WebViewUtil.supportsWebView(context)) {
scope.launch {
launchUI {
context.toast(MR.strings.information_webview_required, Toast.LENGTH_LONG)
}
return response
@@ -1,12 +1,48 @@
package tachiyomi.core.common.util.lang
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT, block)
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.Main, block = block)
@@ -10,7 +10,6 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
@@ -18,14 +17,15 @@ import kotlinx.coroutines.flow.shareIn
class StorageManager(
private val context: Context,
scope: CoroutineScope,
storagePreferences: StoragePreferences,
) {
private val scope = CoroutineScope(Dispatchers.IO)
private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory.get())
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
val changes = _changes.receiveAsFlow()
.flowOn(Dispatchers.IO)
.shareIn(scope, SharingStarted.Lazily, 1)
init {
@@ -43,7 +43,6 @@ class StorageManager(
}
_changes.send(Unit)
}
.flowOn(Dispatchers.IO)
.launchIn(scope)
}