Check available ram before pages prefetch

This commit is contained in:
Koitharu
2023-06-02 15:29:55 +03:00
parent 893ba37c86
commit b1187c611a
3 changed files with 24 additions and 4 deletions

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.util.ext
import android.app.Activity import android.app.Activity
import android.app.ActivityManager import android.app.ActivityManager
import android.app.ActivityManager.MemoryInfo
import android.app.ActivityOptions import android.app.ActivityOptions
import android.content.Context import android.content.Context
import android.content.Context.ACTIVITY_SERVICE import android.content.Context.ACTIVITY_SERVICE
@@ -23,8 +24,6 @@ import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.IntegerRes import androidx.annotation.IntegerRes
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.IntentCompat
import androidx.core.content.PackageManagerCompat
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope import androidx.lifecycle.coroutineScope
@@ -142,6 +141,13 @@ fun Context.isLowRamDevice(): Boolean {
return activityManager?.isLowRamDevice ?: false return activityManager?.isLowRamDevice ?: false
} }
val Context.ramAvailable: Long
get() {
val result = MemoryInfo()
activityManager?.getMemoryInfo(result)
return result.availMem
}
fun scaleUpActivityOptionsOf(view: View): ActivityOptions = ActivityOptions.makeScaleUpAnimation( fun scaleUpActivityOptionsOf(view: View): ActivityOptions = ActivityOptions.makeScaleUpAnimation(
view, view,
0, 0,

View File

@@ -54,9 +54,10 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
suspend fun put(url: String, source: Source): File = withContext(Dispatchers.IO) { suspend fun put(url: String, source: Source): File = withContext(Dispatchers.IO) {
val file = File(cacheDir.get().parentFile, url.longHashCode().toString()) val file = File(cacheDir.get().parentFile, url.longHashCode().toString())
try { try {
file.sink(append = false).buffer().use { val bytes = file.sink(append = false).buffer().use {
it.writeAllCancellable(source) it.writeAllCancellable(source)
} }
check(bytes != 0L) { "No data has been written" }
lruCache.get().put(url, file) lruCache.get().put(url, file)
} finally { } finally {
file.delete() file.delete()

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.reader.domain package org.koitharu.kotatsu.reader.domain
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
@@ -8,6 +9,7 @@ import androidx.collection.LongSparseArray
import androidx.collection.set import androidx.collection.set
import dagger.hilt.android.ActivityRetainedLifecycle import dagger.hilt.android.ActivityRetainedLifecycle
import dagger.hilt.android.lifecycle.RetainedLifecycle import dagger.hilt.android.lifecycle.RetainedLifecycle
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ActivityRetainedScoped import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -27,8 +29,10 @@ import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope
import org.koitharu.kotatsu.core.util.ext.ensureSuccess import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.ramAvailable
import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.ext.withProgress
import org.koitharu.kotatsu.core.util.progress.ProgressDeferred import org.koitharu.kotatsu.core.util.progress.ProgressDeferred
import org.koitharu.kotatsu.local.data.CbzFilter import org.koitharu.kotatsu.local.data.CbzFilter
@@ -47,6 +51,7 @@ import kotlin.coroutines.CoroutineContext
@ActivityRetainedScoped @ActivityRetainedScoped
class PageLoader @Inject constructor( class PageLoader @Inject constructor(
@ApplicationContext private val context: Context,
lifecycle: ActivityRetainedLifecycle, lifecycle: ActivityRetainedLifecycle,
@MangaHttpClient private val okHttp: OkHttpClient, @MangaHttpClient private val okHttp: OkHttpClient,
private val cache: PagesCache, private val cache: PagesCache,
@@ -76,7 +81,7 @@ class PageLoader @Inject constructor(
} }
fun isPrefetchApplicable(): Boolean { fun isPrefetchApplicable(): Boolean {
return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled && !isLowRam()
} }
@AnyThread @AnyThread
@@ -117,6 +122,9 @@ class PageLoader @Inject constructor(
suspend fun convertInPlace(file: File) { suspend fun convertInPlace(file: File) {
convertLock.withLock { convertLock.withLock {
if (context.ramAvailable < file.length() * 2) {
return@withLock
}
runInterruptible(Dispatchers.Default) { runInterruptible(Dispatchers.Default) {
val image = BitmapFactory.decodeFile(file.absolutePath) val image = BitmapFactory.decodeFile(file.absolutePath)
try { try {
@@ -204,6 +212,10 @@ class PageLoader @Inject constructor(
} }
} }
private fun isLowRam(): Boolean {
return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES)
}
private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
CoroutineExceptionHandler { CoroutineExceptionHandler {
@@ -216,6 +228,7 @@ class PageLoader @Inject constructor(
private const val PROGRESS_UNDEFINED = -1f private const val PROGRESS_UNDEFINED = -1f
private const val PREFETCH_LIMIT_DEFAULT = 10 private const val PREFETCH_LIMIT_DEFAULT = 10
private const val PREFETCH_MIN_RAM_MB = 80L
fun createPageRequest(page: MangaPage, pageUrl: String) = Request.Builder() fun createPageRequest(page: MangaPage, pageUrl: String) = Request.Builder()
.url(pageUrl) .url(pageUrl)