Important Steps for Android 17



Important Steps for Android 17 1

Posted by Alice Yuan, Developer Relations Engineer, Ajesh Pai, Developer Relations Engineer, and Fung Lam, Developer Relations Engineer

Whereas app efficiency is usually equated with a easy UI and quick begin occasions, reminiscence serves because the silent basis upon which these seen metrics are constructed. It is no secret that we’re seeing a shift the place machine reminiscence is extra vital than ever. Not solely have we made strides in Android reminiscence optimizations with Android 17, we’re offering the tooling and API assist that will help you keep forward of stricter reminiscence necessities later this yr.

To make sure machine stability, beginning in Android 17, the system will start implementing app reminiscence limits based mostly on the machine’s whole RAM. If an app exceeds these limits, Android will kill the method with no related stack hint.

Past these compelled terminations, unoptimized reminiscence utilization inevitably degrades the consumer expertise. When the app approaches heap reminiscence limits, it triggers frequent rubbish assortment—resulting in noticeable UI stutters. Moreover, when a tool runs out of obtainable reminiscence, the system scrambles to reclaim pages, inflicting CPU pressure, UI latency, and battery drain. If the reminiscence scarcity is just too extreme, it might probably trigger Low Reminiscence Killer (LMK) occasions that abruptly terminate background processes and pressure apps to have gradual chilly begins and lose consumer state.

A condensed model of this weblog submit can also be obtainable in video format, go test it out!

Understanding Android 17 app reminiscence limits

App reminiscence limits are being launched in Android 17 to stop “one unhealthy actor” from destroying the multitasking expertise and stability of the consumer’s total machine.

Here’s a breakdown of the explanations driving this architectural change:

  • Stopping cascading kills: When an app turns into bloated or leaks reminiscence whereas holding a privileged state (e.g. it’s operating a Foreground Service), it’s initially shielded from the system’s Low Reminiscence Killer (LMK). As this single app grows unchecked and hoards RAM, the LMK is compelled to compensate by killing off dozens of smaller, well-behaved cached apps and background jobs to reclaim area for the reminiscence hog.
  • Preserving multitasking and consumer state: When the system is compelled to purge cached apps to accommodate a single leaking course of, the multitasking expertise is severely degraded. Customers returning to prior cached purposes encounter sluggish chilly begins as an alternative of near-instant heat resumes. This inefficiency generates extra CPU pressure and accelerates battery depletion. It will probably additionally destroy the consumer’s context in just lately used apps, reminiscent of scroll positions, navigation stacks, and in-game progress.

To find out in case your app session was impacted by these constraints within the area, you’ll be able to name getDescription() inside ApplicationExitInfo. If the system utilized a restrict, the exit cause is reported as REASON_OTHER and the outline string will include “MemoryLimiter:AnonSwap”. You may as well leverage trigger-based profiling utilizing TRIGGER_TYPE_ANOMALY to mechanically seize heap dumps when the reminiscence restrict is reached. Moreover, Android is actively working to floor extra in-field reminiscence metrics to builders inside the Google Play Console.

We’ve got additionally expanded our reminiscence limits documentation to incorporate native debugging instructions, permitting you to simulate reminiscence constraints in your native surroundings and validate your utility’s conduct beneath any reminiscence restrict enforcement. 

Maximize bytecode optimization with R8

A extremely efficient method to cut back your app’s reminiscence footprint is to allow the R8 optimizer. By shrinking lessons, strategies, and fields into shorter names and stripping out unused code and sources, R8 considerably reduces your app’s reminiscence footprint by minimizing the quantity of resident code required throughout execution. 

R8 minimizes resident code, shrinking the reminiscence footprint and decreasing LMK termination danger. This ends in extra frequent heat begins over gradual chilly begins. Moreover, streamlined bytecode reduces main-thread CPU overhead, immediately chopping ANR charges for a extra fluid consumer expertise. For instance, the digital financial institution Monzo enabled full R8 optimization and noticed a 35% discount of their ANR charge, a 30% enchancment in chilly begin charge, and a 9% discount in total app dimension.

The digital financial institution Monzo enabled full R8 optimization and boosted efficiency metrics by as much as 35%.

To correctly configure R8 in your construct.gradle file:

  • Set isShrinkResources = true and isMinifyEnabled = true.
  • Use proguard-android-optimize.txt as an alternative of the legacy proguard-android.txt, which truly prevents optimizations and is not supported in Android Gradle Plugin 9.
  • Take away android.enableR8.fullMode = false out of your gradle.properties.

In case you are utilizing reflection in your code base, then add Hold guidelines to stop R8 from optimizing these components of the code. Make sure that to scope the hold guidelines narrowly to get the utmost optimization.

To get the utmost optimization, ensure that to observe these finest practices in your hold rule file.

  • Take away international choices like -dontoptimize, -dontshrink, and -dontobfuscate that stop R8 from optimizing your complete codebase 
  • Take away hold guidelines that stop optimizing Android parts like Exercise, Providers, Views or Broadcast receivers.
  • Refine the broad bundle large hold guidelines to focus on solely particular lessons or strategies.

To see extra finest practices, view our hold guidelines documentation.

Library Developer R8 Finest Practices

In case you are a library developer, strictly place the principles your shoppers want into your consumer-rules file, and hold your library’s inner safety guidelines in your proguard-rules.professional file. For extra info on optimize libraries, see Optimization for library authors.

R8 Configuration Analyzer

To audit your R8 optimization, use the Configuration Analyzer. Configuration analyzer reveals the present state of optimization with Obfuscation, Optimization, and Shrinking scores. With configuration analyzer, you too can perceive what number of lessons, strategies or fields are prevented from optimization by every hold rule. Refine these broad bundle large hold guidelines to unlock the utmost optimization.

Utilizing configuration analyzer, you too can establish hold guidelines which are subsuming different hold guidelines, redundant hold guidelines and unused hold guidelines.

The Configuration Analyzer reveals the present state of optimization with Obfuscation, Optimization, and Shrinking scores.

R8 Agent Ability 

You may as well leverage the R8 Agent Ability with Android Studio agent or different AI instruments to resolve misconfigurations and refine your guidelines leading to improved app efficiency. (Insights from AI-driven abilities would require technical verification)

Optimize picture loading

Bitmaps are often the biggest frequent objects residing in your app’s reminiscence. They characterize the ultimate stage of the picture loading course of the place compressed information, like JPEGs or PNGs, are decoded into uncooked pixel information for show. This implies a tiny 100KB compressed picture can balloon into a number of megabytes of RAM as a result of reminiscence consumption is decided by the picture’s pixel dimensions and colour depth. Since bitmap operations are often on the important path to drawing frames, unoptimized photographs trigger extreme reminiscence bloat and UI jank.

Google recommends leveraging picture loading libraries Coil for Kotlin-first initiatives, significantly when creating with Jetpack Compose and Glide for Java-based purposes.

Undertake these 5 finest practices

  1. Downsample photographs: For those who’re loading bitmaps manually, keep away from loading a large picture right into a tiny thumbnail view; use inSampleSize to load a smaller model. Glide and Coil downsamples photographs by default and you’ll configure this downsample technique utilizing DownsampleStrategy and ImageLoader respectively.
  2. Cropping: Keep away from embedding padding immediately into a picture file for letterboxing functions (e.g., making a clear border to develop a picture dimensions). Fairly than baking in these borders, make the most of InsetDrawable or apply padding immediately inside the View or Composable containing the bitmap.
  3. Config: Stability reminiscence and high quality by selecting the best pixel format. Use RGB_565 when transparency is not wanted, which makes use of half the reminiscence of the default ARGB_8888 format. In Glide you’ll be able to configure this by utilizing DecodeFormat and in Coil you should utilize bitmapConfig property.
  4. Prioritize vector drawables: For fundamental geometric belongings, leverage ShapeDrawable as a light-weight different to decoding rasterized bitmaps. By defining these belongings as soon as by way of XML, you guarantee they scale seamlessly throughout all show densities whereas successfully eliminating resource-driven reminiscence bloat.
  5. Reuse: In case your utility manages Bitmaps manually then to reduce reminiscence churn, when a bitmap is not required, the app ought to name bitmap.recycle() and instantly discard the Bitmap reference. For those who use a picture loading library like Glide or Coil, return the bitmap to the library’s managed pool. By offering an present buffer for future reminiscence wants, the pool successfully avoids the overhead of recent allocations.

Try our documentation on Optimizing efficiency for photographs to be taught extra.

Android Studio tooling

You may as well eradicate redundant bitmaps utilizing Android Studio Narwhal 4. Right here is hunt them down in 5 easy steps:

  1. Open the Profiler tab in Android Studio
  2. Click on Heap Dump (or “Analyze Reminiscence Utilization”) and hit report to take a snapshot of your app’s present reminiscence state.
  3. Scan the evaluation outcomes for the yellow warning triangle ⚠️, which Android Studio makes use of to flag duplicate bitmaps being saved a number of occasions. Alternatively, navigate to the profiler header, select “Filter by:” and decide the “Duplicate Bitmaps” setting.
  4. Click on on any flagged entry to open the Bitmap Preview pane, permitting you to see precisely which picture is the repeat offender.
  5. Use that visible affirmation to trace down the redundant loading logic in your code and implement a greater caching technique.

 Search for the yellow warning triangle ⚠️ in heap dumps when utilizing the Android Studio Profiler.

Detect and repair reminiscence leaks with Android Studio

Reminiscence leaks in Android happen when your code holds onto an object’s reference lengthy after its lifecycle has ended. This prevents the Rubbish Collector (GC) from reclaiming that reminiscence, ultimately resulting in sluggish efficiency or OutOfMemoryError (OOM).

Android Studio Panda 3 contains a devoted LeakCanary profiler activity, permitting builders to research real-time reminiscence leaks and map traces inside the IDE.

The LeakCanary profiler activity in Android Studio actively strikes the reminiscence leak evaluation out of your machine to your growth machine, leading to a big efficiency increase in the course of the leak evaluation section as in comparison with on-device leak evaluation.

LeakCanary reminiscence leak evaluation contextualized with Go to declaration for debugging

Moreover, the leak evaluation is now contextualized inside the IDE and absolutely built-in along with your supply code, offering options like go to declaration and different useful code connections that drastically cut back the friction and time required to research and repair reminiscence leaks.

Examples of frequent reminiscence leaks 

Reminiscence leaks happen when an object persists in reminiscence past its meant lifespan. This sometimes occurs because of:

  • Retaining references to Fragments, Actions, or Views which are not in use.
  • Mismanaging Context references.
  • Failing to correctly unregister observers, listeners, and receivers.
  • Creating static references to things which are sure to parts with shorter lifecycles.

Listed here are a number of instance situations:

State of affairs

Compose-based instance

View-based instance

Leaking Context

Instance:
Passing LocalContext.present to a ViewModel

Repair:
Hold Context dependent logic inside the UI layer. For non-UI layers, refactor to make use of dependency injection or observe UI state utilizing Kotlin stream.

Instance:
Storing an Exercise in a companion object or static variable.

Repair:
Don’t maintain static references to UI parts. Refactor to make use of dependency injection or observe UI state utilizing Kotlin stream.

Leaking Listeners

Instance:
Utilizing DisposableEffect to begin a listener however leaving onDispose empty.

Repair:
Carry out the unregistration and cleanup logic contained in the onDispose block.

Instance:
Registering for SensorManager updates and forgetting to unregister.

Repair:
Manually name unregisterListener() in onStop() or onDestroy() lifecycle.

Leaking Views

Instance:
Holding a reference to a legacy View inside an AndroidView and not using a launch technique.

Repair:
Use the launch block of the AndroidView composable to wash up the legacy View.

Instance:
Maintaining a reference to a view binding object after the Fragment is destroyed.

Repair:
Set the binding variable to null contained in the onDestroyView() lifecycle methodology.

Trim reminiscence when app leaves seen state

Android can reclaim reminiscence out of your app or cease your app totally if essential to unencumber reminiscence for important duties, as defined in Overview of reminiscence administration. Android will often reclaim reminiscence out of your app when it’s not seen to the consumer, reminiscent of by discarding a few of your app’s code and information pages in reminiscence or compressing your heap allocations. When the consumer resumes your app and your app tries to entry some reminiscence that’s been reclaimed, the OS will swap that reminiscence again in on demand. This swapping conduct might be gradual, and trigger sudden jank or stutters in your app.

For those who depart it to the OS to determine what reminiscence to reclaim out of your app, you could discover that the OS reclaimed reminiscence that you simply’ll want shortly after resuming your app. As a substitute, your app can voluntarily discard reminiscence allocations that it might probably regenerate later, on demand and at a low price. To take action, you’ll be able to implement the ComponentCallbacks2 interface. You’ll be able to implement onTrimMemory in your Exercise, Fragment, Service, and even your customized Utility class. Utilizing it within the Utility class is very efficient for international cache administration.

The supplied onTrimMemory() callback methodology notifies your app of lifecycle or memory-related occasions that current a superb alternative to your app to voluntarily cut back its reminiscence utilization.

When it comes to reminiscence lifecycle administration, your implementation ought to focus completely on TRIM_MEMORY_UI_HIDDEN and TRIM_MEMORY_BACKGROUND. Since Android 14, the system has ceased delivering notifications for different legacy constants, which have been formally deprecated in Android 15.

TRIM_MEMORY_UI_HIDDEN: This sign signifies that your utility’s UI has transitioned out of the consumer’s view. This offers a possibility to launch substantial reminiscence allocations tied strictly to the interface—reminiscent of Bitmaps, video playback buffers, or complicated animation sources.

TRIM_MEMORY_BACKGROUND: At this stage, your course of is residing within the background and is now a candidate for termination to fulfill the system’s international reminiscence wants. To increase the period your course of stays within the cached state, and cut back the variety of app chilly begins, you need to aggressively launch any sources that may be simply reconstructed as soon as the consumer resumes their session.

import android.content material.ComponentCallbacks2
// Different import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    /**
     * Launch reminiscence when the UI turns into hidden or when system sources turn into low.
     * @param stage the memory-related occasion that's raised.
     */
    override enjoyable onTrimMemory(stage: Int) {

        if (stage >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Launch reminiscence associated to UI parts, reminiscent of bitmap caches.
        }

        if (stage >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Launch reminiscence associated to background processing, reminiscent of by
            // closing a database connection.
        }
    }
}

Notice: The onTrimMemory integration might rely upon SDK assist. As an example, sure video games depend on their sport engine to allow this functionality. Please try the sport reminiscence optimization paperwork.

Superior reminiscence observability with ProfilingManager

To catch and diagnose reminiscence points within the area that can’t be reproduced regionally, you need to leverage the ProfilingManager API. Launched in Android 15, this superior observability API means that you can programmatically gather real-user Perfetto profiles.

For groups that lack a devoted infrastructure to handle and host efficiency artifacts, Crashlytics is exploring a specialised answer to streamline this workflow. They’re inviting builders to present suggestions.

Android 17 introduces new event-driven triggers, most notably TRIGGER_TYPE_OOM and TRIGGER_TYPE_ANOMALY:

  • The OOM set off mechanically collects a Java heap dump on the actual second an OutOfMemoryError crash happens, offering exact allocation states. A collected OOM profile is supplied the following time the app begins and registers the registerForAllProfilingResults callback.
  • The Anomaly set off detects extreme efficiency points, reminiscent of extreme binder spam or breached reminiscence thresholds. The reminiscence anomaly delivers a heap dump simply previous to the system terminating the app.
  val profilingManager = 
applicationContext.getSystemService(ProfilingManager::class.java)
    val triggers = ArrayList()  


    triggers.add(ProfilingTrigger.Builder(
                 ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
    val mainExecutor: Executor = Executors.newSingleThreadExecutor()
    val resultCallback = Shopper { profilingResult ->
        if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
            // add profile consequence to server for additional evaluation          
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } 

    profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

When you’ve collected the heap dump, you’ll be able to obtain the profile from the server, or regionally by way of adb pull and drag and drop the file into the Perfetto UI. To streamline your reminiscence debugging workflow, use the Heap Dump Explorer, that is the brand new default view for heap dumps in Perfetto UI. This instrument offers an intuitive interface for inspecting Java heap dumps, permitting you to visualise object allocation hierarchies, compute retained reminiscence sizes, and establish the shortest path from rubbish assortment root. By leveraging the Heap Dump Explorer, you’ll be able to quickly pinpoint reminiscence leaks, bloated retained objects reminiscent of extreme bitmap allocations, and analyze heap object allocations multi function place.

Use the Heap Dump Explorer’s embedded flamegraph to visually examine and navigate by means of objects with the very best heap allocations.

Conclusion

Optimizing bytecode with R8, adopting picture loading finest practices, and resolving reminiscence leaks are important steps towards delivering a high-quality consumer expertise whereas managing sources successfully beneath strain. Adopting these proactive measures helps preserve app stability and efficiency, stopping sudden terminations whereas safeguarding consumer context. To additional your efficiency experience, discover our revised reminiscence steering.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles