Skip to content

Reducing bitmap memory footprint #5276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 22, 2022

Conversation

ouchadam
Copy link
Contributor

@ouchadam ouchadam commented Feb 21, 2022

Type of change

  • Feature
  • Bugfix
  • Technical
  • Other :

Content

  • Stops the shortcut bitmap creation from always creating new bitmaps by doing the adaptive icon changes as a cachable glide transformation
  • Removes our notification bitmap caches in favour of relying on glide instead (which already has an in memory cache)

Motivation and context

To reduce our memory footprint, currently the app is constantly creating resized bitmaps for the shortcuts each time the room list changes.

Screenshots / GIFs

ACCOUNT BEFORE AFTER
Screenshot_20220221_171347 2022-02-21T17:18:06,838797768+00:00 2022-02-21T17:13:12,441129855+00:00
BEFORE AFTER
Screenshot_20220221_172439 after-smenu
before-shortcut-icon after-shortcut

Tests

  • Add multiple large rooms to an account
  • Using the IDE profiler, perform a heap dump and check the bitmap allocations

Tested devices

  • Physical
  • Emulator
  • OS version(s): 8.1.0

@@ -83,11 +87,7 @@ class ShortcutCreator @Inject constructor(

private fun Bitmap.toProfileImageIcon(): IconCompat {
return if (useAdaptiveIcon) {
val insetBmp = Bitmap.createBitmap(adaptiveIconSize, adaptiveIconSize, Bitmap.Config.ARGB_8888)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was the cause of our extra allocations, the fix was to transform the bitmap transformation into a cachable glide transformation https://github.com./vector-im/element-android/pull/5276/files#diff-1f9afdb46350b185b8e9cfc6698b2885bb19fb15d98c1c65dfedfe5371615d81R173

@ouchadam ouchadam closed this Feb 21, 2022
@github-actions
Copy link

Matrix SDK

Integration Tests Results:

  • [org.matrix.android.sdk.session]
    passed=
  • [org.matrix.android.sdk.account]
    passed=
  • [org.matrix.android.sdk.internal]
    passed=
  • [org.matrix.android.sdk.ordering]
    passed=
  • [org.matrix.android.sdk.PermalinkParserTest]
    passed=

@ouchadam ouchadam reopened this Feb 21, 2022
.asBitmap()
.avatarOrText(matrixItem, iconSize)
.transform(CenterCrop(), AdaptiveIconTransformation(adaptiveIconSize, adaptiveIconOuterSides))
.signature(ObjectKey("adaptive-icon"))
Copy link
Contributor Author

@ouchadam ouchadam Feb 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

caches the transformed icon under with an extra unique key, to avoid overwriting other cached images on the same room url

import javax.inject.Singleton

@Singleton
class NotificationBitmapLoader @Inject constructor(private val context: Context) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the notification helpers have been flattened into a single Notification Loader to help give more context to the usage


@WorkerThread
private fun loadRoomBitmap(path: String): Bitmap? {
return try {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the previous in memory caches have been removed, they were repeating the glide in memory cache and potentially causing leaks (as we never cleaned up the references)

@github-actions
Copy link

github-actions bot commented Feb 21, 2022

Unit Test Results

  84 files  ±0    84 suites  ±0   1m 2s ⏱️ -4s
157 tests ±0  157 ✔️ ±0  0 💤 ±0  0 ±0 
504 runs  ±0  504 ✔️ ±0  0 💤 ±0  0 ±0 

Results for commit bad4579. ± Comparison against base commit 95c00a1.

♻️ This comment has been updated with latest results.

avatarRenderer.shortcutDrawable(GlideApp.with(context), roomSummary.toMatrixItem(), iconSize)
val glideRequests = GlideApp.with(context)
val matrixItem = roomSummary.toMatrixItem()
when (useAdaptiveIcon) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha +1 for the boolean when

@ariskotsomitopoulos
Copy link
Contributor

Nice work Adam 🦾 ! Do you think we should do more tests in different devices & android versions or the fix do not depend that much on that?

@ouchadam
Copy link
Contributor Author

@ariskotsomitopoulos for this case the extra bitmaps were being created as part of the adaptive icon logic which is only applied to android 8+

it's possible that different android versions handle garbage collecting the orphaned bitmap native memory differently but I'm not sure if it's worth testing now that we avoid creating all the extra bitmaps 🤔

Copy link
Member

@ganfra ganfra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, thanks!

@ganfra ganfra added the Z-NextRelease For issues and PRs which should be included in the NextRelease. label Feb 22, 2022
Copy link
Member

@bmarty bmarty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

}
}

override fun hashCode() = Util.hashCode(ADAPTIVE_TRANSFORMATION_ID.hashCode(), Util.hashCode(adaptiveIconSize, Util.hashCode(adaptiveIconOuterSides)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is clearer, but I could not resist to try to rewrite it like:

override fun hashCode() = listOf(ADAPTIVE_TRANSFORMATION_ID.hashCode(), adaptiveIconSize, floatToIntBits(adaptiveIconOuterSides))
                .fold(0) { acc, i -> Util.hashCode(acc, i) }

Please just ignore me :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the glide transformation api is a bit clunky, I didn't want to stray tooo far from the examples 😅


override fun equals(other: Any?): Boolean {
return if (other is AdaptiveIconTransformation) {
other.adaptiveIconSize == adaptiveIconSize && other.adaptiveIconOuterSides == adaptiveIconOuterSides
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just compare hashCodes here?

@bmarty bmarty merged commit 211e1c2 into develop Feb 22, 2022
@bmarty bmarty deleted the feature/adm/limit-inmemory-image-caches branch February 22, 2022 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Z-NextRelease For issues and PRs which should be included in the NextRelease.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants