Android Memory Optimization Series:
- Android Code Memory Optimization Suggestions - Android (Official)
- Android Code Memory Optimization Suggestions - Java (Official)
- Android Code Memory Optimization Suggestions - Android Resources
- Android Code Memory Optimization Suggestions - OnTrimMemory
onTrimMemory(int level), introduced in Android 4.0 (API 14), hints when an app should reduce unnecessary memory usage. Trimming caches can help the system retain more background processes, but does not guarantee process survival or a faster next return.
This article keeps the Q&A format to explain callback delivery, integration, and cleanup. Decide what to retain by resource purpose, and make sure the UI can recover what was released.
Technical review: October 13, 2026. Five old memory-pressure levels are no longer delivered from Android 14 (API 34), and their constants were deprecated in API 35. UI_HIDDEN and BACKGROUND remain useful for state-related memory management. The Launcher/Contacts examples below are checked against AOSP
android-4.4.4_r2and retained as historical code, followed by a current cache example.
0. What is the role of the onTrimMemory callback?
The level gives a cleanup hint. It does not report every change in free RAM and is not a guaranteed final opportunity to save data before process death. Release resources that are no longer needed without breaking active work.
The two levels still used today were both introduced in API 14:
| Level | Meaning and response |
|---|---|
TRIM_MEMORY_UI_HIDDEN |
The current process no longer displays UI. Reduce rebuildable UI-only caches while preserving data needed by ongoing work. |
TRIM_MEMORY_BACKGROUND |
The process has entered the background LRU state. Release more easily rebuilt resources; this alone does not establish critical device-wide memory pressure. |
The following five describe historical behavior only: delivery stopped in API 34; the constants were deprecated in API 35.
While the App is Running:
| Historical level | Introduced | Historical meaning |
|---|---|---|
TRIM_MEMORY_RUNNING_MODERATE |
API 16 | Initial device memory-pressure hint to a running process. |
TRIM_MEMORY_RUNNING_LOW |
API 16 | Greater memory pressure while the process is running. |
TRIM_MEMORY_RUNNING_CRITICAL |
API 16 | Severe memory pressure while the process is running. |
While the App is Cached:
| Historical level | Introduced | Historical meaning |
|---|---|---|
TRIM_MEMORY_MODERATE |
API 14 | Near the middle of the background LRU list. |
TRIM_MEMORY_COMPLETE |
API 14 | Near the end of the background LRU list. |
These hints never guaranteed that an app could not be killed. Use threshold comparisons appropriate to your policy, and do not expect every level in sequence. See ComponentCallbacks2 and its Android 16 source.
1. Which components can implement onTrimMemory?
Application, Activity, Service, and ContentProvider provide onTrimMemory(int level). Override it when needed and follow the superclass-call contract.
Distinguish the two Fragment APIs:
- The old
android.app.Fragmenthas this callback from API 14. The platform Fragment API was deprecated in API 28. - Current
androidx.fragment.app.Fragmenthas no same-named callback to override directly. Let the host or a shared cache owner handle trimming, or explicitly register a separate callback and unregister it at the end of its scope. Do not apply the platform Fragment’s method list to AndroidX unchanged.
Implementing ComponentCallbacks2 on an ordinary class does not subscribe it automatically. Register it through Context.registerComponentCallbacks() and pair this with unregisterComponentCallbacks(). Application-level registration retains the callback, so avoid accidentally retaining an Activity or View through it. The API name starts with lowercase: onTrimMemory, not OnTrimMemory.
2. What resources should I release?
Define ownership, capacity budgets, and rebuild costs during design. Typical candidates include unused memory caches, rebuildable previews, and listeners or requests whose work has ended. Preserve unsaved user state and data needed by background work.
Disk and memory caches differ: deleting files mainly frees storage, not the Java heap, and large synchronous disk operations do not belong in this callback. Setting a View to GONE does not destroy it; inspect parent, listener, and queue references. Fragment View references also follow their own View lifecycle rather than waiting for a trim event.
The following two historical examples are followed by a current integration example.
2.1 Example: Releasing Views (Launcher)
These method excerpts come from AOSP Launcher2, android-4.4.4_r2, with field names matched to that tag. They depend on the rest of Launcher2 and are not standalone application code. The old MODERATE notification no longer arrives on Android 14+, so its trigger is not a current solution.
1 |
|
1 | public void onTrimMemory() { |
1 | public void clearAllWidgetPages() { |
1 |
|
This path does more than hide content: it cancels preview tasks, removes widget-page children, clears layout listeners and layer settings, and marks pages for reloading. GONE alone cannot do this, and dropping references does not guarantee an immediate GC or process-memory reduction. The reusable idea is to separate cleanup of rebuildable content from restoration, not to copy the old threshold or launcher internals.
2.2 Example: Clearing Caches (Contacts)
These excerpts are from ContactsCommon’s ContactPhotoManagerImpl (android-4.4.4_r2), with debug logging omitted:
1 |
|
The source clears a pending-request map and two memory-cache layers. Clearing the map does not interrupt I/O already in progress, and background results can refill a cache. Handle cancellation, stale results, and reloading together when adapting this pattern. MODERATE remains a historical trigger.
2.3 Current Example: A Budgeted UI Bitmap Cache
This complete API 19+ example caches rebuildable software Bitmaps associated with the UI configuration. The caller supplies a positive pixel budget based on measurement. getAllocationByteCount() is available from API 19; this budget counts allocated pixels, not every key, object header, or container, and is not a hard process-memory limit.
1 | import android.app.Application; |
The example clears the same UI cache at UI_HIDDEN or higher levels, avoiding duplicate branches. The old RUNNING_* levels do not trigger this example. A null result from get() requires the image to be rebuilt or loaded again. The cache also clears on configuration changes so content can be rebuilt for the new configuration. onLowMemory() is included for older systems only and is not called on API 34+.
Create and manage this object on the main thread, normally under an application-level owner. If its scope ends before the process does, call dispose() and stop submitting work or data to it. Do not register a new object on every Activity transition. External owners must not arbitrarily call recycle() while a Bitmap is cached. evictAll() drops cache references without recycling Bitmaps still held elsewhere.
Clearing does not stop data production: cancel unnecessary prefetch or ignore stale results to prevent immediate refilling. A measured policy may retain selected entries or trim in stages for smoother recovery. Do not apply this UI-only cache policy wholesale to playback, uploads, or other business data. See LruCache.
3. onTrimMemory vs. onStop
onStop() concerns one Activity; UI_HIDDEN concerns UI visibility for the current process. Moving from Activity A to B can stop A while the process still has visible UI. Multi-process apps must also distinguish their processes rather than treating one callback as global visibility state.
Page-visible resources can follow paired onStart/onStop handling. Bounded caches shared across pages can have a longer-lived owner. Not every UI resource must wait for UI_HIDDEN before release.
Do not rely on UI_HIDDEN always occurring before onStop. These notifications have different scopes, and the public APIs do not provide a universal ordering contract for synchronizing application logic. Cleanup should be repeatable and recoverable, without relying on a trim callback before process termination.
4. onTrimMemory vs. onLowMemory
onLowMemory() is the older, coarse low-memory notification. Comparing it with COMPLETE in the old article described cleanup intensity, not identical triggering conditions.
It is no longer called from API 34 and was deprecated in API 35; see ComponentCallbacks. It is therefore not a replacement for old trim-pressure notifications that no longer arrive. Combine supported state callbacks, component lifecycles, and ordinary cache budgets instead.
5. Why bother?
The app should respond to this callback when appropriate. Passing a level manually executes local cleanup; it does not declare that process state to the system.
Reducing unused memory can help the system retain background processes, but reclamation also depends on component state, dependencies, and global pressure. It is not a fixed “walk LRU and kill the largest app first” rule. Nor does a surviving process guarantee a hot start or a fixed 2–3x speedup: an Activity that needs recreation can still undergo a warm start, and aggressive trimming can increase reload costs.
Distinguish cold, warm, and hot startup and measure the same device, version, and interaction path. See App startup time. Balance lower retained memory with the cost of restoring the experience.
6. Typical Use Cases
6.1 Long-lived Apps
Long-running apps such as a launcher can clear unused preview pages and UI caches while retaining the ability to rebuild them. Labels such as launcher, phone, or security center do not guarantee process survival; system roles and implementations determine priority. Respond to system notifications and the app’s lifecycle rather than repeatedly calling trim methods to keep a process alive.
6.2 Apps with Background Services
Music playback and downloads can continue to need data when the UI is hidden. Reduce display-only cover art or preview caches without releasing the player, transfer buffers, or connections still in use.
A Service is not a process-survival guarantee. Follow current background-execution and foreground-service rules, and manage each resource by purpose. Validate backgrounding, return to UI, internal Activity transitions, and configuration changes while recording the levels actually received. On Android 14+, do not wait for the five retired pressure levels to establish that cleanup works.
About Me && Blog
Here are my introduction and related links. I welcome conversations with fellow developers and opportunities to learn from one another.
- About me: contact details and WeChat groups.
- Blog navigation: a guide to this blog’s articles.
- Android Performance Optimization: Essential Skills and Tools: recommendations and contributions are welcome through WeChat.
- Android Performance community: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.
If you want to go fast, go alone. If you want to go far, go together.
