AOSP Foundations
4 min read

Project Butter & Project Svelte

Understand the system-level optimizations introduced by Project Butter and Project Svelte to improve Android's rendering performance and memory efficiency.

Early versions of Android faced intense criticism for UI stuttering and high memory consumption compared to competing mobile platforms. To address these fundamental platform issues, Google launched two major internal initiatives: Project Butter (Android 4.1) and Project Svelte (Android 4.4).

Understanding these projects provides deep insight into Android's rendering pipeline and low-level memory management strategies.

Project Butter: Rendering Synchronization

Introduced in Android 4.1 (Jelly Bean), Project Butter was aimed at eliminating UI "jank" and achieving a consistent 60 frames per second (fps) rendering target.

The Problem: Desynchronized Rendering

Before Jelly Bean, the Android UI thread operated independently of the hardware display's refresh rate. An app might begin calculating its layout and drawing operations arbitrarily. If the app took too long to draw a frame, it would miss the display's hardware refresh window, causing the screen to display the previous frame again, resulting in a noticeable stutter.

The Butter Solutions

  1. Vsync (Vertical Synchronization): Project Butter introduced a system-wide "heartbeat" tied to the display hardware. The SurfaceFlinger (Android's system compositor) and the application's UI thread were synchronized using a Vsync signal. Apps are now triggered to begin rendering exactly when the Vsync signal fires, giving them the maximum possible time (16.6ms for 60Hz displays) to compute the frame before the next refresh.
  2. Choreographer: A new Java API called Choreographer was introduced to coordinate the timing of animations, input, and drawing. It acts as the bridge between the hardware Vsync signal and the app's UI thread.
  3. Triple Buffering: If an app's UI thread temporarily exceeds the 16.6ms window, triple buffering allows the GPU to read from one buffer, while the app writes to a second buffer, and a third buffer is kept in reserve. This prevents the entire rendering pipeline from stalling during sudden processing spikes.
// How the framework hooks into Vsync via Choreographer
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        // This executes exactly when the Vsync signal fires
        updateAnimationState();
    }
});

You can profile Vsync and rendering times directly from the ADB shell:

adb shell dumpsys gfxinfo <package_name> framestats

Project Svelte: Memory Efficiency

Introduced in Android 4.4 (KitKat), Project Svelte aimed to drastically reduce Android's memory footprint, allowing the OS to run efficiently on entry-level devices with as little as 512MB of RAM.

The Problem: Memory Thrashing

On low-RAM devices, the system frequently ran out of memory. The ActivityManager had to aggressively kill background apps to reclaim RAM. When users switched back to those apps, they had to cold-start from scratch, causing massive CPU spikes and severe UI lag.

The Svelte Solutions

  1. The low_ram Property: Project Svelte introduced the ro.config.low_ram build property. When enabled on entry-level hardware, the framework completely disables memory-intensive visual features (like live wallpapers), reduces the maximum number of background processes, and simplifies animations to conserve RAM.
  2. zRAM Integration: Svelte popularized the use of zRAM. Instead of using a traditional swap partition on slow flash storage (which ruins performance), zRAM creates a compressed block device in RAM. When the system needs memory, it compresses idle memory pages and moves them to zRAM. This effectively increases available memory at the cost of slight CPU overhead for compression/decompression.
  3. onTrimMemory() API: Developers were provided with the ComponentCallbacks2.onTrimMemory() API. The OS actively signals running apps when system memory runs low, allowing well-behaved apps to release cached data voluntarily before the OS is forced to kill the process entirely via the Low Memory Killer.
// App responding to system-level memory pressure
@Override
public void onTrimMemory(int level) {
    if (level == TRIM_MEMORY_RUNNING_CRITICAL) {
        // The LMK is about to kill us! 
        // Drop all cached bitmaps immediately!
        bitmapCache.clear();
    }
}

You can check if your specific device is running in a Svelte "low RAM" mode by querying the system properties:

adb shell getprop ro.config.low_ram