08 9월 2022
Posted by Niharika Arora, Developer Relations Engineer
The Android operating system brings the power of computing to everyone. This vision applies to all users, including those on entry-level phones that face real constraints across data, storage, memory, and more.
This was especially important for us to get right because, when we first announced Android (Go edition) back in 2017, people using low-end phones accounted for 57% of all device shipments globally (IDC Mobile Phone Tracker).
Year | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 |
Release | Android 8 | Android 9 | Android 10 | Android 11 | Android 12 | Android 13 |
Min RAM | 512MB | 512MB | 512MB | 1GB | 1GB | 2GB |
Faster App Launches | Longer Battery Life | Easier App Sharing | More Privacy Control |
Phases | Description | ||||||||||
Define | Before starting any optimization effort, it’s important to define the goals. Key Performance Indicators (KPIs) have to be defined for the app.
| ||||||||||
Breakdown | Once KPIs are defined, the next steps could be to break down a given KPI into individual signal metrics.
| ||||||||||
Benchmark | Benchmark or measure the KPI values and individual metrics to identify current performance. If KPI targets are met, things are good. If not → identify the bottlenecks by looking at the individual breakdowns. | ||||||||||
Repeat the process | After optimizing a certain bottleneck go back and benchmark the metrics again to see if the KPI targets are met. If not, repeat the process. If yes, great job! | ||||||||||
Add Regular regression test | That either runs for every change or in some frequency to identify regressions in KPIs. It is more difficult to debug and find sources of regressions or bugs than to not allow them to get into the codebase. Don’t allow the changes that fail the KPI goals unless the decision is to update the KPI targets.
|
GBoard used the onTrimMemory() signal to trim unneeded memory while it goes in the background and there is not enough memory to keep as many background processes running as desired, for example, trimming unneeded memory usage from expressions, search, view cache or openable extensions in background. It helped them reduce the number of times being low memory killed and the average background RSS. Resident Set Size(RSS) is basically the portion of memory occupied by your app process that is held in main memory (RAM). To know more about RSS, please refer here.
Typically this is useful for loading large assets or ML models.
Google apps used the tools to identify and fix memory issues which helped reduce the memory usage/footprint of the app. This reduction allowed other components of the app to run without adding additional memory pressure on the system.
An example from Gboard app is about View leaks
A specific case is caching subviews, like this:
void onKeyboardViewCreated(View keyboardView) {
this.keyButtonA = keyboardView.findViewById(...);
...
}
The |keyboardView| might be released at some time, and the |keyButtonA| should be assigned as null appropriately at some time to avoid the view leak.
Lessons learned:
- Always add framework/library updates after analyzing the changes and verifying its impact early on.
- Make sure to release memory before assigning new value to a pointer pointing to other object allocation in heap in Java. (native backend java objects)
For example :
In Java it should be ok to do
ClassA obj = new ClassA("x");
// ... something
obj = new ClassB("y");
GC should clean this up eventually.
if ClassA allocates native resources underneath and doesn't cleanup automatically on finalize(..) and requires caller to call some release(..) method, it needs to be like this
ClassA obj = new ClassA("x");
// ... something
// Explicit cleanup.
obj.release();
obj = new ClassB("y");
else it will leak native heap memory.
- Optimize your bitmaps: Large images/drawables usually consume more memory in the app. Google apps identified and optimized large bitmaps that are used in their apps.
Lessons learned :
- Prefer Lazy/on-demand initializations of big drawables.
- Release view when necessary.
- Avoid using full colored bitmaps when possible.
For example: Gboard’s glide typing feature needs to show an overlay view with a bitmap of trails, which can only has the alpha channel and apply a color filter for rendering.
// Creating the bitmap for trails.
trailBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
...
// Setup paint for trails.
trailPaint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[] {
0, 0, 0, 0, (color >> 16) & 0xFF,
0, 0, 0, 0, (color >> 8) & 0xFF,
0, 0, 0, 0, color & 0xFF,
0, 0, 0, 1, 0
})));
...
// onDraw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (trailBitmap != null) {
canvas.drawBitmap(trailBitmap, 0, 0, trailPaint);
}
}
A screenshot of glide typing on Gboard
- Check and only set the alpha channel for the bitmap for complex custom views used in the app. This saved them a couple of MBs (per screen size/density).
- While using Glide,
- The ARGB_8888 format has 4 bytes/pixel consumption while RGB_565 has 2 bytes/pixel. Memory footprint gets reduced to half when RGB_565 format is used but using lower bitmap quality comes with a price too. Whether you need alpha values or not, try to fit your case accordingly.
- Configure and use cache wisely when using a 3P lib like Glide for image rendering.
- Try to choose other options for GIFs in your app when building for Android (Go edition) as GIFs take a lot of memory.
- The aapt tool can optimize the image resources placed in res/drawable/ with lossless compression during the build process. For example, the aapt tool can convert a true-color PNG that does not require more than 256 colors to an 8-bit PNG with a color palette. Doing so results in an image of equal quality but a smaller memory footprint. Read more here.
- You can reduce PNG file sizes without losing image quality using tools like pngcrush, pngquant, or zopflipng. All of these tools can reduce PNG file size while preserving the perceptive image quality.
- You could use resizable bitmaps. The Draw 9-patch tool is a WYSIWYG editor included in Android Studio that allows you to create bitmap images that automatically resize to accommodate the contents of the view and the size of the screen. Learn more about the tool here.
In the next part of this blog, we will talk about the best practices on Startup latency, app size and the tools used by Google apps to identify and fix performance issues.