From 85a1b33e838dc58be9add13dd82a277188c12e44 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Wed, 2 Apr 2025 22:30:40 +0200 Subject: [PATCH] debug: glide sizing, memory cache clear --- .../channel/calls/MediaFetchObjectHandler.kt | 14 ++++++-- .../aves/decoder/AvesAppGlideModule.kt | 35 +++++++++++++++++++ lib/services/media/media_fetch_service.dart | 17 +++++++-- lib/widgets/about/data_usage.dart | 2 +- lib/widgets/debug/cache.dart | 14 +++++++- 5 files changed, 75 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt index 4f0a501f8..ead151e9f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFetchObjectHandler.kt @@ -1,6 +1,8 @@ package deckers.thibault.aves.channel.calls import android.content.Context +import android.os.Handler +import android.os.Looper import androidx.core.net.toUri import com.bumptech.glide.Glide import deckers.thibault.aves.channel.calls.Coresult.Companion.safe @@ -21,7 +23,8 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "getEntry" -> ioScope.launch { safe(call, result, ::getEntry) } - "clearSizedThumbnailDiskCache" -> ioScope.launch { safe(call, result, ::clearSizedThumbnailDiskCache) } + "clearImageDiskCache" -> ioScope.launch { safe(call, result, ::clearImageDiskCache) } + "clearImageMemoryCache" -> ioScope.launch { safe(call, result, ::clearImageMemoryCache) } else -> result.notImplemented() } } @@ -47,11 +50,18 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler }) } - private fun clearSizedThumbnailDiskCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { + private fun clearImageDiskCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { Glide.get(context).clearDiskCache() result.success(null) } + private fun clearImageMemoryCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { + Handler(Looper.getMainLooper()).post { + Glide.get(context).clearMemory() + } + result.success(null) + } + companion object { const val CHANNEL = "deckers.thibault/aves/media_fetch_object" } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt b/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt index 52336f25c..22c5f12f7 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt @@ -2,6 +2,7 @@ package deckers.thibault.aves.decoder import android.content.Context import android.net.Uri +import android.text.format.Formatter import android.util.Log import com.bumptech.glide.Glide import com.bumptech.glide.GlideBuilder @@ -10,9 +11,17 @@ import com.bumptech.glide.annotation.GlideModule import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.load.ImageHeaderParser import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter +import com.bumptech.glide.load.engine.bitmap_recycle.LruArrayPool +import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool +import com.bumptech.glide.load.engine.cache.DiskCache +import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory +import com.bumptech.glide.load.engine.cache.LruResourceCache +import com.bumptech.glide.load.engine.cache.MemorySizeCalculator import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser import com.bumptech.glide.module.AppGlideModule import com.bumptech.glide.request.RequestOptions +import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.MimeTypes.isVideo import deckers.thibault.aves.utils.StorageUtils @@ -23,6 +32,30 @@ class AvesAppGlideModule : AppGlideModule() { override fun applyOptions(context: Context, builder: GlideBuilder) { // hide noisy warning (e.g. for images that can't be decoded) builder.setLogLevel(Log.ERROR) + + // sizing + val memorySizeCalculator = MemorySizeCalculator.Builder(context).build() + builder.setMemorySizeCalculator(memorySizeCalculator) + val size: Int = memorySizeCalculator.bitmapPoolSize + if (size > 0) { + builder.setBitmapPool(LruBitmapPool(size.toLong())) + } else { + builder.setBitmapPool(BitmapPoolAdapter()) + } + builder.setArrayPool(LruArrayPool(memorySizeCalculator.arrayPoolSizeInBytes)) + builder.setMemoryCache(LruResourceCache(memorySizeCalculator.memoryCacheSize.toLong())) + + val diskCacheSize = DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE + val internalCacheDiskCacheFactory = InternalCacheDiskCacheFactory(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, diskCacheSize.toLong()) + builder.setDiskCache(internalCacheDiskCacheFactory) + + fun toMb(bytes: Int) = Formatter.formatFileSize(context, bytes.toLong()) + Log.d( + LOG_TAG, "Glide disk cache size=${toMb(diskCacheSize)}" + + ", memory cache size=${toMb(memorySizeCalculator.memoryCacheSize)}" + + ", bitmap pool size=${toMb(memorySizeCalculator.bitmapPoolSize)}" + + ", array pool size=${toMb(memorySizeCalculator.arrayPoolSizeInBytes)}" + ) } override fun registerComponents(context: Context, glide: Glide, registry: Registry) { @@ -34,6 +67,8 @@ class AvesAppGlideModule : AppGlideModule() { override fun isManifestParsingEnabled(): Boolean = false companion object { + private val LOG_TAG = LogUtils.createTag() + // request a fresh image with the highest quality format val uncachedFullImageOptions = RequestOptions() .format(DecodeFormat.PREFER_ARGB_8888) diff --git a/lib/services/media/media_fetch_service.dart b/lib/services/media/media_fetch_service.dart index 3e84f8d16..6f05eaf38 100644 --- a/lib/services/media/media_fetch_service.dart +++ b/lib/services/media/media_fetch_service.dart @@ -55,7 +55,9 @@ abstract class MediaFetchService { int? priority, }); - Future clearSizedThumbnailDiskCache(); + Future clearImageDiskCache(); + + Future clearImageMemoryCache(); bool cancelRegion(Object taskKey); @@ -255,9 +257,18 @@ class PlatformMediaFetchService implements MediaFetchService { } @override - Future clearSizedThumbnailDiskCache() async { + Future clearImageDiskCache() async { try { - return _platformObject.invokeMethod('clearSizedThumbnailDiskCache'); + return _platformObject.invokeMethod('clearImageDiskCache'); + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + } + + @override + Future clearImageMemoryCache() async { + try { + return _platformObject.invokeMethod('clearImageMemoryCache'); } on PlatformException catch (e, stack) { await reportService.recordError(e, stack); } diff --git a/lib/widgets/about/data_usage.dart b/lib/widgets/about/data_usage.dart index 9ab3dd2b7..67d5d52c9 100644 --- a/lib/widgets/about/data_usage.dart +++ b/lib/widgets/about/data_usage.dart @@ -88,7 +88,7 @@ class _AboutDataUsageState extends State with FeedbackMixin { onPressed: () async { await storageService.deleteTempDirectory(); await storageService.deleteExternalCache(); - await mediaFetchService.clearSizedThumbnailDiskCache(); + await mediaFetchService.clearImageDiskCache(); imageCache.clear(); _reload(); setState(() {}); diff --git a/lib/widgets/debug/cache.dart b/lib/widgets/debug/cache.dart index a84e2d9be..419e3c0c7 100644 --- a/lib/widgets/debug/cache.dart +++ b/lib/widgets/debug/cache.dart @@ -87,7 +87,19 @@ class _DebugCacheSectionState extends State with AutomaticKee ), const SizedBox(width: 8), ElevatedButton( - onPressed: mediaFetchService.clearSizedThumbnailDiskCache, + onPressed: mediaFetchService.clearImageDiskCache, + child: const Text('Clear'), + ), + ], + ), + Row( + children: [ + const Expanded( + child: Text('Glide memory cache: ?'), + ), + const SizedBox(width: 8), + ElevatedButton( + onPressed: mediaFetchService.clearImageMemoryCache, child: const Text('Clear'), ), ],