Avoid unnecessary coroutine context switch

This commit is contained in:
tarsin committed 2023-01-01 16:58:34 +08:00
1 parent 4a4efe4725
commit a3263ab4af
5 files changed
+122 -72

No files matched your search

@@ -58,12 +58,10 @@ import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IntIdGenerator
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Cache
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
@@ -118,36 +116,34 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
GlobalScope.launch {
withContext(Dispatchers.IO) {
try {
val downloadLocation = Settings.getDownloadLocation()
if (Settings.getMediaScan()) {
CommonOperations.removeNoMediaFile(downloadLocation)
} else {
CommonOperations.ensureNoMediaFile(downloadLocation)
}
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
launchIO {
try {
val downloadLocation = Settings.getDownloadLocation()
if (Settings.getMediaScan()) {
CommonOperations.removeNoMediaFile(downloadLocation)
} else {
CommonOperations.ensureNoMediaFile(downloadLocation)
}
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
// Clear temp files
try {
clearTempDir()
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
// Clear temp files
try {
clearTempDir()
} catch (t: Throwable) {
t.printStackTrace()
ExceptionUtils.throwIfFatal(t)
}
}
GlobalScope.launch {
launchIO {
theDawnOfNewDay()
}
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0))
}
private suspend fun theDawnOfNewDay() {
private fun theDawnOfNewDay() {
if (!Settings.getRequestNews()) {
return
}
@@ -159,22 +155,20 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
EhCookieStore.KEY_IPD_PASS_HASH
)
) {
withContext(Dispatchers.IO) {
val referer = EhUrl.REFERER_E
val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build()
val call = okHttpClient.newCall(request)
try {
call.execute().use { response ->
val responseBody = response.body
val body = responseBody.string()
val html = EventPaneParser.parse(body)
if (html != null) {
showEventPane(html)
}
val referer = EhUrl.REFERER_E
val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build()
val call = okHttpClient.newCall(request)
try {
call.execute().use { response ->
val responseBody = response.body
val body = responseBody.string()
val html = EventPaneParser.parse(body)
if (html != null) {
showEventPane(html)
}
} catch (e: Throwable) {
e.printStackTrace()
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
}
@@ -24,12 +24,10 @@ import com.hippo.util.ExceptionUtils
import com.hippo.util.HashCodeUtils
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.IOUtils
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSource
@@ -193,7 +191,7 @@ object EhTagDatabase {
@OptIn(DelicateCoroutinesApi::class)
@JvmStatic
fun update() {
GlobalScope.launch(Dispatchers.IO) {
launchIO {
updateInternal()
}
}
@@ -27,11 +27,10 @@ import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.widget.SearchDatabase
import com.hippo.scene.StageActivity
import kotlinx.coroutines.Dispatchers
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
abstract class SearchBarScene : ToolbarScene() {
@@ -311,12 +310,12 @@ abstract class SearchBarScene : ToolbarScene() {
@SuppressLint("NotifyDataSetChanged")
private fun updateSuggestions(scrollToTop: Boolean = true) {
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Default) {
viewLifecycleOwner.lifecycleScope.launch {
val suggestions = mutableListOf<Suggestion>()
mergedSuggestionFlow().collect {
suggestions.add(it)
}
withContext(Dispatchers.Main) {
withUIContext {
mSuggestionList = suggestions
mSuggestionAdapter?.notifyDataSetChanged()
}
@@ -85,6 +85,8 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.preference.toggle
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
@@ -93,15 +95,12 @@ import eu.kanade.tachiyomi.util.view.copy
import eu.kanade.tachiyomi.util.view.popupMenu
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileInputStream
import java.io.IOException
@@ -134,29 +133,27 @@ class ReaderActivity : EhActivity() {
AppConfig.getExternalTempDir().toString() + File.separator + mCacheFileName
val cachefile = File(filepath)
val resolver = contentResolver
lifecycleScope.launch {
withContext(Dispatchers.IO) {
var `is`: InputStream? = null
var os: OutputStream? = null
try {
`is` = FileInputStream(cachefile)
os = resolver.openOutputStream(uri)
IOUtils.copy(`is`, os)
} catch (e: IOException) {
e.printStackTrace()
} finally {
IOUtils.closeQuietly(`is`)
IOUtils.closeQuietly(os)
runOnUiThread {
Toast.makeText(
this@ReaderActivity,
getString(R.string.image_saved, uri.path),
Toast.LENGTH_SHORT
).show()
}
lifecycleScope.launchIO {
var `is`: InputStream? = null
var os: OutputStream? = null
try {
`is` = FileInputStream(cachefile)
os = resolver.openOutputStream(uri)
IOUtils.copy(`is`, os)
} catch (e: IOException) {
e.printStackTrace()
} finally {
IOUtils.closeQuietly(`is`)
IOUtils.closeQuietly(os)
runOnUiThread {
Toast.makeText(
this@ReaderActivity,
getString(R.string.image_saved, uri.path),
Toast.LENGTH_SHORT
).show()
}
cachefile.delete()
}
cachefile.delete()
}
}
}
@@ -253,7 +250,9 @@ class ReaderActivity : EhActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.colorMode = if (Image.isWideColorGamut && readerPreferences.wideColorGamut().get()) ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT else ActivityInfo.COLOR_MODE_DEFAULT
window.colorMode = if (Image.isWideColorGamut && readerPreferences.wideColorGamut()
.get()
) ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT else ActivityInfo.COLOR_MODE_DEFAULT
if (savedInstanceState == null) {
onInit()
} else {
@@ -279,7 +278,7 @@ class ReaderActivity : EhActivity() {
mCurrentIndex = if (mPage >= 0) mPage else mGalleryProvider!!.startPage
}
lifecycleScope.launch(Dispatchers.Main) {
lifecycleScope.launchUI {
mGalleryProvider!!.state.collect {
if (it == PageLoader.STATE_READY) {
setGallery()
@@ -0,0 +1,60 @@
package eu.kanade.tachiyomi.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)
fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.IO, block = block)
fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job =
launchIO { withContext(NonCancellable, block) }
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)
suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) =
withContext(NonCancellable, block)