# Actionable strategies for reducing your app's Bitmap memory footprint - Speakers: Fung Lam (Developer Relations Engineer) - Track: Tools & Performance - Video: https://www.youtube.com/watch?v=KD_55WdEYj0 - Page: https://developer.android.com/events/dev-summit/talks/actionable-strategies-for-reducing-your-apps-bitmap-memory-footprint ## About the talk Managing Bitmap memory is critical for mitigating Out-Of-Memory (OOM) risks in visually rich Android apps. While developers know to reduce image sizes, achieving long-term app stability comes from mastering two other key pillars: Reuse and Recycle. This session delivers a practical playbook for optimizing memory performance using modern tools, open-source libraries like Glide and Coil, and system-level APIs. In this session, you will learn how to diagnose real-world memory regressions using Android Vitals, implement advanced caching (Reuse) patterns with Glide and Coil, and proactively release memory (Recycle) using the onTrimMemory callback. ## Summary - Google Play is introducing memory performance thresholds enforced in February 2027. Exceeding the 90th percentile limit directly impacts your app's health assessment and can reduce visibility on the Play Store. - Bitmaps expand by dimensions and color depth when decoded, making them the largest memory consumers in most apps. Just a few full-resolution images can take hundreds of megabytes. - Downsample images and decode only visible regions to reduce memory consumption. Request matching image dimensions via CDN or use APIs like `BitmapRegionDecoder` rather than loading oversized full-resolution images. - Multiple image loaders multiply default memory heap cache usage and duplicate stored Bitmaps. Standardize on one loader or set explicit memory budgets across existing loaders. - Release image memory when your app moves to the background by handling `onTrimMemory`. Stop background polling and animations in `onStop` to prevent background memory growth. ## Resources - [Android memory documentation](https://developer.android.com/memory) ## Topics bitmap-optimization, android-memory, android-vitals, image-caching, memory-profiler ## Transcript ### Introduction & Bitmap memory overview **Fung Lam** [00:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=5s) Yes, we are about 1 hour before the lunch, so please stay with me. [00:10](https://www.youtube.com/watch?v=KD_55WdEYj0&t=10s) I'm Fung Lam, developer relations engineer in Android based here in London. [00:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=17s) Yeah, thank you. [00:18](https://www.youtube.com/watch?v=KD_55WdEYj0&t=18s) Before I start, may I ask a question? [00:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=20s) How many of you bought a new phone this year? [00:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=25s) Wow. [00:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=25s) Wow. [00:26](https://www.youtube.com/watch?v=KD_55WdEYj0&t=26s) Be honest, I will not blame you if you are not buying, Pixel. [00:32](https://www.youtube.com/watch?v=KD_55WdEYj0&t=32s) Well, it's more expensive, right, for the new phone. [00:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=37s) Yep, that's why some of you didn't buy a new phone. [00:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=40s) Why is it more expensive? [00:41](https://www.youtube.com/watch?v=KD_55WdEYj0&t=41s) Do you know? [00:43](https://www.youtube.com/watch?v=KD_55WdEYj0&t=43s) No. [00:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=44s) RAM. [00:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=44s) RAM, yeah, yeah. [00:46](https://www.youtube.com/watch?v=KD_55WdEYj0&t=46s) Yeah, RAM is really the challenge for Android and for us, for developer. [00:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=50s) That's why today I'm here to tell how we could overcome the challenge on memory, on RAM. [00:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=58s) Earlier in Google I/O and in Google I/O Connect, we had some sessions about memory, from how the operation system allocate the memory, how you could profile your application, how you could optimize your code using R8, and of course, I did cover a bit about Bitmap at that time. [01:15](https://www.youtube.com/watch?v=KD_55WdEYj0&t=75s) You could see my photos in the screenshot. [01:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=77s) Memory optimization is one of the large domain, which across different area like Android runtime, garbage collections, performance tooling, object memory allocation, compiler, etc. And in this session, in these 30 minutes, I will give you some information about how to optimize Bitmap memory. [01:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=98s) You may ask, why, why Bitmap? [01:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=100s) Why we should focus on Bitmap memory? [01:42](https://www.youtube.com/watch?v=KD_55WdEYj0&t=102s) Because one image worth more than a thousand word. [01:46](https://www.youtube.com/watch?v=KD_55WdEYj0&t=106s) That's true, I will explain to you why. [01:48](https://www.youtube.com/watch?v=KD_55WdEYj0&t=108s) On a phone screen, let's say on a Pixel 7, or Pixel 9, or Pixel 11 Pro version, the screen resolution is around 2,500 1,200, 1,400. [02:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=123s) So, for each pixel, we carry 32 bit of the color depth. [02:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=128s) So, for the image of the full screen, oh, I do not have enough finger, but, like, it will take about 15 MB of memory, 50 MB. [02:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=137s) So, but, like, a word in the OS system is like 64-bit, that means is 8 byte. [02:22](https://www.youtube.com/watch?v=KD_55WdEYj0&t=142s) A thousand words is only eight, 8 MB. [02:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=145s) Yeah, 8K. [02:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=147s) So, definitely one image worth more than a thousand words. [02:31](https://www.youtube.com/watch?v=KD_55WdEYj0&t=151s) Yep, this is important. [02:32](https://www.youtube.com/watch?v=KD_55WdEYj0&t=152s) One image is already 15 MB. [02:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=155s) What if your app have 10, 100 image? [02:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=158s) Well, it's a large number. [02:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=160s) So, actually Bitmap is always, usually the biggest memory consumer in your application. [02:47](https://www.youtube.com/watch?v=KD_55WdEYj0&t=167s) When the image decode into memory, as I mentioned, is it's expand as the dimension and the color depth. [02:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=174s) This is not hey, how the file size it's is in, JPEG or is it in PNG format. [02:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=178s) No, it will be decode and use the memory. [03:01](https://www.youtube.com/watch?v=KD_55WdEYj0&t=181s) So, just a few full-resolution image will easily take up hundreds of megabyte of the memory on your device. [03:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=191s) And secondly, when the memory is very tight, the operation system then will have to spend more power to handle the memory consumption. [03:22](https://www.youtube.com/watch?v=KD_55WdEYj0&t=202s) This will lead to like the UI is janky. [03:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=205s) Or, the OS may also, kill your app to free the memory. [03:30](https://www.youtube.com/watch?v=KD_55WdEYj0&t=210s) Or your app will also encounter Out-Of-Memory error, Out-Of-Memory exception. [03:36](https://www.youtube.com/watch?v=KD_55WdEYj0&t=216s) Luckily, the good news is, this face usually is a huge win with little effort. [03:43](https://www.youtube.com/watch?v=KD_55WdEYj0&t=223s) Most memory spikes come from common mistake, which could be avoidable, and we could fixing this without any tradeoff of the image quality. [03:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=234s) Now you know, Bitmap memory is important for your application, not only for make it smooth, but also about stability and user retention. [04:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=247s) So, today we will go through five simple part to keep your app running smoothly. [04:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=252s) First, we will look into the Google Play technical quality, the new technical quality requirement, which include upcoming memory limit and where you could check the limit easily. [04:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=265s) Second, I will share a case my study about 500 Android application, how they use them in memory, are they doing it in a right way, or is there something we could learn from? [04:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=278s) Afterward, I will show you the principle how to optimize Bitmap memory usage. [04:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=284s) And finally, we will share some tools that could help you to check your own app. ### Google Play quality standards & common issues **Fung Lam** [04:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=291s) First, the Google Play new quality requirement. [04:55](https://www.youtube.com/watch?v=KD_55WdEYj0&t=295s) How often do you visit the Google Play Console? [04:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=298s) Every month? [05:00](https://www.youtube.com/watch?v=KD_55WdEYj0&t=300s) Every weeks? [05:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=303s) Never? [05:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=305s) Those laughing means yes, right? [05:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=307s) No? [05:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=307s) No. [05:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=308s) Did you visit the Android Vitals part? [05:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=311s) Yes, yes. [05:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=311s) Yep, so you definitely, you know something before I make this. [05:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=316s) So, in Google Play, there are new quality requirements on performance threshold. [05:22](https://www.youtube.com/watch?v=KD_55WdEYj0&t=322s) We Android and Play wants an user to use app with good quality. [05:29](https://www.youtube.com/watch?v=KD_55WdEYj0&t=329s) So, this threshold is also some, something, we try to bring the best to the end user. [05:36](https://www.youtube.com/watch?v=KD_55WdEYj0&t=336s) In simple words, that is, "Hey, your app, when it is running in background, or in observable foreground surface, then your 90 percentile of the memory usage shouldn't be above this threshold. [05:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=353s) Or otherwise, your app may have reduced visibility on the Play Store, or something." [06:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=363s) And the enforcement will be starting in February 2027 on apps and games. [06:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=371s) Exceeding this threshold directly impact your app health assessment. [06:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=376s) And so, make sure you go to Google Play console and check, "Hey, what is my 90 percentile today?" [06:22](https://www.youtube.com/watch?v=KD_55WdEYj0&t=382s) And make the improvement before the deadline. [06:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=387s) This is the animation. [06:28](https://www.youtube.com/watch?v=KD_55WdEYj0&t=388s) "Hey, if you go to the Google Play console, you could see the, yep, there is Android Vitals. [06:32](https://www.youtube.com/watch?v=KD_55WdEYj0&t=392s) There's a part of memory. [06:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=394s) And you could see the 90 percentile. [06:36](https://www.youtube.com/watch?v=KD_55WdEYj0&t=396s) And if you click on it, yep, you could see over time the average of the memory consumption. [06:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=404s) And there are also different tiers by RAM, by form factor, by screen density, by OS version, which help you to isolate memory bottleneck on specific entry-level device, or on specific app releases. [07:00](https://www.youtube.com/watch?v=KD_55WdEYj0&t=420s) Now, I know, for you may say, "Hey, thank you for telling me my apps is it over the threshold. [07:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=425s) But really, I don't know what's wrong. [07:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=427s) Would you help?" [07:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=428s) Yes, that's why I'm here to help you. [07:14](https://www.youtube.com/watch?v=KD_55WdEYj0&t=434s) I will try to make it easy for you, just like, hey, if if you're watching TV, and then your TV got no video or no audio, what will you do? [07:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=443s) You take out your TV manual, go to the very last few pages, which is usually the troubleshooting guide, saying, "Hey, if your TV have no sound, try this button, or try that button." [07:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=453s) Yep, similarly, today, I had I identified some common issue. [07:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=459s) And hopefully, this is your troubleshooting guide. [07:43](https://www.youtube.com/watch?v=KD_55WdEYj0&t=463s) Normally, when we read the TV manual, we should go from the page one, which is, usually the diagno-- diagnostic toolkits to really help you to understand how the TV work. [07:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=474s) But now, that I know those are less interesting, so today, please let allow me to go from the troubleshooting first, and we will come back later. [08:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=483s) And to be honest, this is really how I read the TV manual. [08:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=488s) To uncover the most frequent Bitmap allocation Bitmap memory issue around Android ecosystem, I researched around 500 apps, popular apps, in Android. [08:19](https://www.youtube.com/watch?v=KD_55WdEYj0&t=499s) I really run the app, start the app, log in, go to different pages, go deep, and come back. [08:26](https://www.youtube.com/watch?v=KD_55WdEYj0&t=506s) And I test the app on different RAM, different devices also, like, Android Go device with 2 gig of memory, some entry-level 4 gig, some, mid-level four, 6 gig to 8 gig, or some premium foldable with a really large screen, very good screen quality, and 16 gig of memory. [08:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=525s) We measured the memory usage on this device when when on different app status, capturing the heap dump, and use, different tools, including Perfetto AI Skill, to analyze the allocation, to identify common anti-pattern, yep, the common mistake we make, at scale. [09:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=543s) And so, this is how the memory changed in different activity states. [09:10](https://www.youtube.com/watch?v=KD_55WdEYj0&t=550s) And with this analysis, we identify over 15 common issue, ranged from oversized image, from increasing background caches. [09:18](https://www.youtube.com/watch?v=KD_55WdEYj0&t=558s) So this is like the core, case study we will be sharing, we'll be exploring today. ### Case studies: Sizing and decoding Bitmaps **Fung Lam** [09:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=567s) Yep, all the case study is about this I'm sharing now is about the 500-app research. [09:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=574s) As you are here, I think we all love Android, right? [09:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=578s) We all love Android. [09:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=579s) Let's say there is, one of us also love Android, but he or she is not an expert in Android development. [09:48](https://www.youtube.com/watch?v=KD_55WdEYj0&t=588s) So, he make an Android, app for ordering Android figurine. [09:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=593s) But there are some mistake. [09:55](https://www.youtube.com/watch?v=KD_55WdEYj0&t=595s) Let's talk Let's see maybe this is the first mistake. [09:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=598s) "Hey, in the product page, we have a two-column layout showing different Android figurine. [10:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=605s) The For each column, the image is around 400 x 400 pixel in size. [10:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=611s) However, the app, actually, is downloading a one image from the server, no matter my how large my screen is. [10:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=620s) It's downloading a 3000 x 3000 image. [10:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=625s) Yeah, because like the developer, "Hey, Android is so beautiful, so amazing. [10:28](https://www.youtube.com/watch?v=KD_55WdEYj0&t=628s) I want to show the best to the user." [10:30](https://www.youtube.com/watch?v=KD_55WdEYj0&t=630s) But actually, no, because like, hey, it is capped by the dimension of the image view. [10:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=635s) So, even if you download the large image, it is not showing the best to the user. [10:41](https://www.youtube.com/watch?v=KD_55WdEYj0&t=641s) In addition, this large image caused additional memory. [10:46](https://www.youtube.com/watch?v=KD_55WdEYj0&t=646s) And even if we downsample it, the process of downsample also used additional memory. [10:52](https://www.youtube.com/watch?v=KD_55WdEYj0&t=652s) How should we help this developer to fix it? [10:55](https://www.youtube.com/watch?v=KD_55WdEYj0&t=655s) Well, Well, if you know about CDN, CDN actually could handle some image operation, like cropping, resize, rotate, applying filter, make it from square to round image. [11:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=672s) By HTTP request, CDN could handle something. [11:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=676s) So, even if your server-side is only hosting one image, you could use CDN to send a resized image to your device. [11:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=685s) So, number 1, yeah when when you request the image, try to use CDN, so you will request the image as the ImageView size. [11:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=694s) But if not possible, like oh it could be sometimes a little bit bigger than ImageView, remember to downsample to resize it in the code. [11:42](https://www.youtube.com/watch?v=KD_55WdEYj0&t=702s) So, when it is load into the memory, it is loading a smaller image, not loading the full resolution of the image. [11:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=710s) Here is a code sample for Coil. ```kotlin // 1. Ask CDN for the exact UI card dimensions (e.g. 540x675) val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp" // 2. Enforce exact downsampling + Hardware Bitmap config AsyncImage( model = ImageRequest.Builder(LocalContext.current) .data(sizedUrl) .size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL! .precision(Precision.EXACT) // Downsample to exact card size .bitmapConfig(Bitmap.Config.HARDWARE) .build(), contentDescription = product.title ) ``` [11:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=713s) You could pass the size as the wording in yellow. [11:56](https://www.youtube.com/watch?v=KD_55WdEYj0&t=716s) Yeah, you could pass the size as HTTP in a HTTP request. [12:02](https://www.youtube.com/watch?v=KD_55WdEYj0&t=722s) And then, here, in the highlighted code, could you see the highlight, yes, yep, don't use `Size.ORIGINAL`. [12:09](https://www.youtube.com/watch?v=KD_55WdEYj0&t=729s) Yeah, use the width and height, so it will be passed as the parameter. [12:13](https://www.youtube.com/watch?v=KD_55WdEYj0&t=733s) Use the precision to downsample the image to exact size. [12:18](https://www.youtube.com/watch?v=KD_55WdEYj0&t=738s) And if possible, use the `Bitmap.Config.HARDWARE`, which also use less memory as the image will be stored in VRAM directly. [12:26](https://www.youtube.com/watch?v=KD_55WdEYj0&t=746s) Similar, on this app say, hey, they release a new version of Android collectibles, so there is a long image which user could swipe left and right to see different design of the new Android figure. [12:42](https://www.youtube.com/watch?v=KD_55WdEYj0&t=762s) But there's also another problem here. [12:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=765s) For the long image, a lot of it, actually, is beyond the screen, which means, actually, user couldn't see it, but the image is loaded in the memory. [12:55](https://www.youtube.com/watch?v=KD_55WdEYj0&t=775s) Could we do it better? [12:56](https://www.youtube.com/watch?v=KD_55WdEYj0&t=776s) Yes. [12:57](https://www.youtube.com/watch?v=KD_55WdEYj0&t=777s) We could chop it into a smaller, smaller image and show only the image shown on the screen will be loaded. [13:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=785s) Those beyond the screen will not be loaded, or will be unloaded. [13:09](https://www.youtube.com/watch?v=KD_55WdEYj0&t=789s) So, it will be using less memory. [13:13](https://www.youtube.com/watch?v=KD_55WdEYj0&t=793s) Similarly, oh, so in this case, it saved around 60% of memory. [13:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=800s) Of course, if you trim into smaller rectangle, it will save you more. [13:24](https://www.youtube.com/watch?v=KD_55WdEYj0&t=804s) But similarly, oh if a user want want to provide a feature for user, hey, you could zoom in and zoom in to see every detail of the Android figurine, some common mistake is they really load, again, a very huge image, so it could be zoomed and zoom again. [13:42](https://www.youtube.com/watch?v=KD_55WdEYj0&t=822s) But, no, you don't have to do this. [13:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=824s) A better way is when the user zoom in the original already loaded lower low-resolution image, and when the user stop pinching, you calculate the coordinate and then load the full resolu-- only load that area, a calculated area from the high-resolution image. [14:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=847s) So, only those visible area will be loaded into the memory. [14:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=852s) How how to do this? [14:13](https://www.youtube.com/watch?v=KD_55WdEYj0&t=853s) Oh, there is an API called BitmapRegionDecoder, which helps you to load a specific region of an image. [14:21](https://www.youtube.com/watch?v=KD_55WdEYj0&t=861s) And of course, there are also some tools and library could help you. [14:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=865s) You could search it on GitHub. [14:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=867s) Some comics app or some map are already using this. [14:31](https://www.youtube.com/watch?v=KD_55WdEYj0&t=871s) You could use them as a reference. [14:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=873s) And in additional, we are human, we make mistakes. [14:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=877s) AI also make mistakes. [14:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=879s) So, to prevent us to make mistakes, we could also add some limit when we load image. [14:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=885s) For example, in this case, I could say, "Hey, let's use the window size or the screen dimension as a limit and put it in a min function, min of request width or the bound width." ```kotlin // Query active window bounds instead of hardcoding 1080 / 1920 val bounds = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(activity).bounds // Clamp target dimensions so Bitmap NEVER exceeds the active window val safeWidth = minOf(requestedWidthPx, bounds.width()) val safeHeight = minOf(requestedHeightPx, bounds.height()) val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config) ``` [14:59](https://www.youtube.com/watch?v=KD_55WdEYj0&t=899s) So, with this function, whenever I load an image, will it would be never existing the boundary. [15:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=905s) So, it would I would it will not loading an image larger than the screen resolution. [15:09](https://www.youtube.com/watch?v=KD_55WdEYj0&t=909s) Remember, only decode the re- only decode the visible region of the image that will save your memory. ### Managing memory, duplicates & cache **Fung Lam** [15:19](https://www.youtube.com/watch?v=KD_55WdEYj0&t=919s) And again, say hey, this app, actually, is not from one developer. [15:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=923s) Actually, it's for multiple contributor and they may be using different tooling. [15:28](https://www.youtube.com/watch?v=KD_55WdEYj0&t=928s) For example, part of it is using oh sorry. [15:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=933s) Let me start again. [15:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=935s) Same for this app. [15:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=937s) Hey, I in the product page, I have a list of product and I saw this image, I add it into the shopping cart. [15:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=945s) A few minutes after, I go to checkout, hey, the same image appear again. [15:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=951s) But is this really the same image or, actually, this is two different object in the memory? [15:59](https://www.youtube.com/watch?v=KD_55WdEYj0&t=959s) Yep, one of the common mistake we found is like, hey, that the app is loading a lot of image. [16:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=965s) However, they're they're also having a lot of duplicate image in the memory. [16:09](https://www.youtube.com/watch?v=KD_55WdEYj0&t=969s) If you're using image loader, yep, actually, you could pass a key for doing the caching. [16:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=976s) So when when the same image is used, you could you be used the already allocated Bitmap object rather than creating a new object. [16:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=985s) In Android Studio, you could use Memory Profiler to find, hey, is my app is having duplicate Bitmap? [16:32](https://www.youtube.com/watch?v=KD_55WdEYj0&t=992s) I covered it in my Google I/O session, so you could go back to YouTube to watch the video. [16:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=997s) And if I'm not using Android Studio, there are also adb command for you to capture the heap dump and then you could use Perfetto heap graph SQL language to query if my app is having duplicate Bitmap. [16:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1011s) So this is a code example in Coil, how to how do I add a key for Bitmap caching. ```kotlin // Strip volatile query tokens (?from=list / ?sig=...) for cache key val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?")) // On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List! AsyncImage( model = ImageRequest.Builder(context) .data(productUrl) .placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse! .memoryCacheKey(canonicalKey) .diskCacheKey(productUrl.substringBefore("?")) .build(), contentDescription = product.title ) ``` [16:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1018s) And now, yep. [16:59](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1019s) I back to the previous example. [17:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1023s) Hey, my app is having different developer. [17:06](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1026s) Some of it is using Views, so this is using Glide as a image loader. [17:10](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1030s) And some of them are using Coil as a because they're using Compose. [17:15](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1035s) So is this I you just mentioned oh, image loader help us to do caching, so is this anything wrong if I have more than one image loader? [17:24](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1044s) Definitely, there's something wrong. [17:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1047s) So for the image loader, by default, they will use X% of the heap memory size as the default cache size. [17:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1054s) So if you have one image loader, is used X% of your memory. [17:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1060s) If you have two image loader, it will be 2X of the memory. [17:44](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1064s) If you have three image loader, it will be 3X. [17:47](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1067s) And if your image loader does not share the same pool, the same thing will happen. [17:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1073s) Like in the product page, I load an image using Glide and then in the checkout page, the same image will be loaded using Coil. [18:02](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1082s) Still, there will be two image in the memory. [18:04](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1084s) That's no good. [18:06](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1086s) Yep, so to fix it, of course, ideally, we just use one image loader or one shared pool for the caching. [18:13](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1093s) But what if this is like, oh, a lot of engineering work, I couldn't do it. [18:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1097s) Oh, a easier fix will be, hey, I measure how many percent of the image will be handled by this image loader, how many percent of the image will be handled by another image loader and set weight for different image loader, just like this code. ```kotlin val totalBudgetBytes = computeAdaptiveCacheBytes(context) val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil) // Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B) val loaderABudget = (totalBudgetBytes * 0.70).toInt() val loaderBBudget = (totalBudgetBytes * 0.30).toInt() // Approach B (Simple Fix without stats): Divide by number of loaders! val simpleSplitBudget = totalBudgetBytes / numberOfLoaders MemoryCache.Builder() .maxSizeBytes(simpleSplitBudget) .build() ``` [18:30](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1110s) I could set `loaderABudget` to be 70%, `loaderBBudget` to be 30%, so that's another way. [18:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1119s) If you really don't know how how much image will be handled by each each loader, still still, it is better to just divide. [18:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1131s) Hey, I have two image loader, divide by 2; three image loader, divide by 3 rather than using the default value because that will be overused the memory. [19:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1143s) And let's say, hey, still, in the product page, I we expect user to scroll to see more of the product. [19:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1152s) So normally, we I we may hey, let's cache around 50 image or 50 MB of image. [19:19](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1159s) But what if the app is go goes to background? [19:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1163s) Oh, if the app is in background, so the user may may not be browsing. [19:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1167s) If the user is not browsing, should I still keep this amount this mem- this amount of cache image in the memory? [19:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1175s) No, we should release it. [19:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1178s) Yep, we should be respecting the onTrimMemory callback. [19:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1185s) Yep, a lot of apps, actually, currently, they don't implement or they don't respect the onTrimMemory callback. [19:52](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1192s) For onTrimMemory callback, there are two signal we should take care of. [19:55](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1195s) One is `UI_HIDDEN`, which is like the user has pressed the home button, and it's not immediately after they press the home button, is a period of time after they press the Home button, which means, "Oh, your app is actually in a state that there's no visible UI to the user." [20:13](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1213s) And we could expect, "Hey, user is not using your app. [20:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1216s) You should release the resources to the OS." [20:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1220s) So we should trim the cache size. [20:24](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1224s) And the other one is `TRIM_MEMORY_BACKGROUND`, which means that app is already in the background for a long time, and now the OS really need some resources. [20:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1233s) In this stage, you should release your memory as much as possible, almost 100%. [20:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1239s) By doing this, yeah, you also reduce your chance, reduce your risk of your app being killed by the OS. [20:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1245s) Because like, "Hey, they want memory, you give them, then that OS handle it." [20:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1250s) If no, then the OS got no choice, they have to do something. ### Preventing background memory growth **Fung Lam** [20:57](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1257s) So another major mistake I see is like, "Hey, I just mentioned that your app, if the app, if it's goes to background, it should be using less memory." [21:06](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1266s) However, I did see apps actually using more memory in the background, more memory. [21:14](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1274s) Why? [21:15](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1275s) That's why I do the research. [21:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1276s) I tried to find out why. [21:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1277s) And one of the reason is, "Hey, in their app, they have some animation or they have some carousel. [21:26](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1286s) For example, they download a new image every 15 seconds, and they show a new new image to the user." [21:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1294s) However, the developer did not stop the animation when the apps goes to background. [21:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1300s) So, even the app is in background, the code is still running, is still getting new image, decoding the new image, using more memory. [21:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1310s) So this is actually something app shouldn't be doing. [21:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1314s) Yep. [21:57](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1317s) We should be stopped all the animation, the caching, or unrelated work when the app is going to background. [22:05](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1325s) So if you are aware of the activity life cycle, so there are onPause, `onStop` event, make sure you implement the `onStop`, onPause, `onDestroyView` to stop the animation, yep, to stop the image pulling requests, or other things relate to the work that doesn't need user attention. ```kotlin override fun onStop() { super.onStop() // 1. Stop animations so they don't decode next frames in background! bannerAnimationView.cancelAnimation() animatedImageDrawable?.stop() // 2. Cancel next-image buffering / prefetch queues! nextImagesBufferingJob?.cancel() Glide.with(this).pauseRequests() } override fun onDestroyView() { super.onDestroyView() bannerAnimationView.clearAnimation() // Release resources! } ``` ### The 3Rs framework for memory health **Fung Lam** [22:29](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1349s) I know I have talked so many case with you, but I said, "I have more than 15 cases." [22:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1354s) I know if I talk share all of the cases with you may forgot everything. [22:39](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1359s) You will just go back when you go back to home, you will just search Google, "Hey, how do I fix my memory issues?" [22:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1365s) So I make it easy for you to remember. [22:48](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1368s) Yep. [22:49](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1369s) I will link it to a very well known 3R framework to help you to memorize them. [22:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1374s) This 3R framework, I think everyone should have heard of it. [22:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1378s) The first one is reduce, how to reduce memory when loading an image. [23:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1383s) Of course, like I mentioned, we could downsample, get the right dimension of the image when we get from the server. [23:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1391s) We optimize the color depth, or use hardware Bitmap if possible. [23:16](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1396s) And if if you are not using CDN, try use CDN. [23:19](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1399s) So when we are getting the image from the server, it's just getting the image that's best fit the size. [23:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1405s) Reuse, so we use cache so that the image load into memory will be reused, and we are not having duplicate Bitmap image in our memory. [23:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1417s) And, of course, try not to have multiple image loader in your code, because that's less efficient. [23:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1425s) Reduce, reuse, and what is the remaining one? [23:48](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1428s) Do you know? [23:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1430s) Recycle. [23:50](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1430s) Retry? [23:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1431s) Recycle, yes, recycle. [23:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1433s) Yep, we learned reduce, reuse, and recycle in primary school, although we don't learn coding in primary school. [23:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1438s) Yep, recycle, yep. [24:01](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1441s) Yeah, it's about how to release the memory so other other application or the OS could use the memory. [24:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1448s) There there are things like about recycle. [24:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1451s) First, I mentioned there's onTrimMemory event say there are two signal the OS will send to you when your UI is hidden or the OS really need resources. [24:21](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1461s) Yep, try to handle this request. [24:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1463s) And, of course, if you are having animation, image loader, or some some other work that's happening in foreground, make sure you stop it when when this is like on a onPause, `onStop` event. [24:36](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1476s) So reduce, reuse, recycle. [24:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1477s) Easy to remember, right? ### Developer diagnostic toolkit & AI analysis **Fung Lam** [24:41](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1481s) Now we are going back to the less interesting part of the TV manual. [24:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1485s) How do we use different toolkit to detect to measure this memory issue effectively? [24:52](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1492s) So the first one is the Google Play Console, Android Vitals, and Reporting API. [24:58](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1498s) Yep, check the 90th percentile in Google Play Console. [25:02](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1502s) Where are you now? [25:03](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1503s) Or use the Reporting API if you don't visit the web page. [25:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1507s) You could still use API to get the value. [25:10](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1510s) Yep, make sure, yep, the threshold is 200 MB and 400 MB. [25:14](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1514s) Make sure you are not over the threshold. [25:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1517s) There are also ADB command to help you like to capture the heap dump or to send event to simulate, hey, my app is now in a `UI_HIDDEN` stage. ```shell adb shell dumpsys meminfo # watch Native Heap! adb shell am send-trim-memory UI_HIDDEN ``` [25:29](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1529s) If you are using Android Studio, good. [25:31](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1531s) Yet, this is always a good friend for you. [25:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1533s) You could use the Android Studio profiler to capture the heap dump, to have a UI to see what is actually what actually your memory usage, what actually is a memory allocation. [25:47](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1547s) And of course, we talk about AI with Gemini in Android Studio or other Android skills available. [25:53](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1553s) It's help you to analyze the heap dump and using Perfetto SQL traces to help you to find the Bitmap, to normally tell who are the cache owner. [26:04](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1564s) It is my image load load by Glide or load by Coil? [26:08](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1568s) Are there any other memory leak in my application? [26:14](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1574s) I know some of you say, "Hey, actually I am not using IDE anymore. [26:18](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1578s) I'm a prompt engineer. [26:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1580s) I use, yeah, I just sending prompt with AI agent to do the code." [26:24](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1584s) Yes, I know. [26:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1585s) I also do some vibe coding during my free time. [26:27](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1587s) So, I'm share this with you just a sample. [26:30](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1590s) Yeah, this is just part of the prompt I use for my 500 app research. [26:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1595s) Yeah, just part of it. [26:36](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1596s) You know the screen is not large. [26:38](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1598s) If I put more, you could barely see it. [26:40](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1600s) But like it give you an idea how you could use AI to automate, to capture the memory usage, including heap dump and to analyze and to provide findings with you. [26:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1614s) And yep, I could say here, at one more sentence, yeah, "Please also provide me the code fix for my code." [27:01](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1621s) Yeah, I hope this could reduce your fears, say, "Hey, memory is a large domain. [27:06](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1626s) I didn't study operating system or computer system architecture in computer science in my university. [27:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1632s) But actually, this is not that scary, this is not that difficult." [27:15](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1635s) Yep, This is easy for you to analyze your app. [27:20](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1640s) Please try it. [27:21](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1641s) But of course, AI will make mistake, so fundamental knowledge about how memory usage is also how OS allocate memory is still important. [27:31](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1651s) That's why come to my talk and watch our video on YouTube. ### Key takeaways and summary **Fung Lam** [27:35](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1655s) So, to recap, we have some takeaway. [27:41](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1661s) First, reduce. [27:42](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1662s) Make sure you get the right size of the image. [27:45](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1665s) You split the image into smaller image not to load everything on the memory, yeah, just to load the image that is visible to the user into the memory. [27:54](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1674s) Use hardware `bitmapConfig` or `RGB_565` whenever possible. [27:59](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1679s) Reuse, so make sure you have a cache strategy, use image loader to help you to reduce the number of duplicate Bitmap in the memory. [28:11](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1691s) And of course, don't use multiple Bitmap image, even if you are tried to overwrite the default cache size of those image loader. [28:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1703s) And we not reuse, recycle. [28:25](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1705s) Yep. [28:29](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1709s) Your app's memory management should adapt to the app state. [28:34](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1714s) If the UI is hidden, yep, you should use less memory. [28:37](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1717s) If the OS really ask you to release the memory, release as much as you can. [28:43](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1723s) And stop draining the resources, no matter it's Bitmap or power or computational resources. [28:48](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1728s) Yep, stop stop the banner timer, stop the animation, stop the image pooling. [28:56](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1736s) And lastly, make sure you monitor. [28:59](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1739s) Go to Google Play Console regularly. [29:01](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1741s) Make sure this is a practice. [29:02](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1742s) Yeah, this will help you to understand, hey, what is the current status of my 90th percentile of the memory usage of my app in the whole user population? [29:12](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1752s) I That will also help you to identify issue early. [29:14](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1754s) Yep. [29:17](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1757s) To profiling locally, you could use Android Studio memory profiler. [29:21](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1761s) We could We also have some ADB command. [29:23](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1763s) We also have Android skill. [29:24](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1764s) But make sure if you want to see the statistics for the real-world user, and Google Play Console is the place for you. [29:33](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1773s) And there's also Google Play Reporting API for you to get it using API not going to the Play Console by logging. [29:41](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1781s) As always, if you want to learn more, visit developer.android.com/memory. [29:47](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1787s) Reduce, reuse, and recycle with this. [29:51](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1791s) We could build a better You could I hope we together could build a better Android's ecosystem to overcome the challenge that we are facing. [30:00](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1800s) Thank you and happy coding. [30:02](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1802s) I will be having office hours soon outside, so if you have questions, feel free to come. [30:07](https://www.youtube.com/watch?v=KD_55WdEYj0&t=1807s) And the next speaker well, have some time, so please wait for the next speaker to on stage and thank you very much.