diff --git a/gleap/src/main/java/io/gleap/GleapChatMessage.java b/gleap/src/main/java/io/gleap/GleapChatMessage.java index f2ed65d..721bef7 100644 --- a/gleap/src/main/java/io/gleap/GleapChatMessage.java +++ b/gleap/src/main/java/io/gleap/GleapChatMessage.java @@ -90,15 +90,13 @@ public LinearLayout getComponent(Activity activity) { } public void clearComponent() { - if (this.avatarBitmap != null) { - this.avatarBitmap.recycle(); - this.avatarBitmap = null; - } - - if (this.topImageBitmap != null) { - this.topImageBitmap.recycle(); - this.topImageBitmap = null; - } + // These bitmaps come from GleapImageLoader, which still holds each one + // in its in-memory cache. The loader owns their lifecycle (it evicts + // under memory pressure and lets GC reclaim native memory); recycling + // a cache-shared entry here leaves a recycled bitmap in the cache that + // throws when onTrimMemory measures it. Just drop the reference. + this.avatarBitmap = null; + this.topImageBitmap = null; this.layout = null; } diff --git a/gleap/src/main/java/io/gleap/GleapImageLoader.java b/gleap/src/main/java/io/gleap/GleapImageLoader.java index 4004f1b..c6c42ee 100644 --- a/gleap/src/main/java/io/gleap/GleapImageLoader.java +++ b/gleap/src/main/java/io/gleap/GleapImageLoader.java @@ -48,7 +48,9 @@ public Thread newThread(Runnable runnable) { private static final LruCache cache = new LruCache(cacheSizeKb()) { @Override protected int sizeOf(String key, Bitmap bitmap) { - return bitmap.getByteCount() / 1024; + // A consumer that recycled a shared entry would otherwise throw + // IllegalStateException here during evictAll/trimToSize. + return bitmap.isRecycled() ? 0 : bitmap.getByteCount() / 1024; } }; private static boolean trimCallbacksRegistered = false; @@ -242,10 +244,14 @@ private static synchronized void registerTrimCallbacks(Context context) { applicationContext.registerComponentCallbacks(new ComponentCallbacks2() { @Override public void onTrimMemory(int level) { - if (level >= TRIM_MEMORY_BACKGROUND) { - cache.evictAll(); - } else if (level >= TRIM_MEMORY_UI_HIDDEN) { - cache.trimToSize(cache.size() / 2); + // Runs on the main thread; must never crash the host app. + try { + if (level >= TRIM_MEMORY_BACKGROUND) { + cache.evictAll(); + } else if (level >= TRIM_MEMORY_UI_HIDDEN) { + cache.trimToSize(cache.size() / 2); + } + } catch (Exception ignored) { } } @@ -255,7 +261,10 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public void onLowMemory() { - cache.evictAll(); + try { + cache.evictAll(); + } catch (Exception ignored) { + } } }); trimCallbacksRegistered = true;