criticalsenior2022

Twitter Android

The Background Ghost

Twitter appeared in every user's top battery consumers — even when they hadn't opened the app in days.

The Incident

In late 2022, Twitter's Android app was flagged widely as one of the top battery drainers on the platform. Users reported significant battery depletion overnight, even after force-stopping the app. Battery Historian logs showed constant background wakeups. The system had become a ghost that refused to sleep.

Evidence from the Scene

  • Battery drain continued overnight with the screen off and app in background
  • Battery Historian showed CPU wakeups every 15–30 seconds
  • Force-stopping the app temporarily halted the drain — until the next launch
  • The drain persisted even with all notifications disabled in Android settings
  • Android 12+ automatically applied battery restriction to the app after repeated offenses

The Suspects

3 of these are the real root causes. The others are plausible-sounding distractors.

WakeLock acquired on network response but never released on the failure path

Background service polling the timeline API every 30 seconds

AlarmManager using ELAPSED_REALTIME_WAKEUP bypassing Doze mode entirely

Bitmap cache holding references to all decoded tweet images

Room database writes running without transaction blocks

RecyclerView not recycling ViewHolders on large timeline lists

The Verdict

Real Root Causes

  • WakeLock acquired on network response but never released on the failure path

    A WakeLock not released in every code path (including exception handlers) keeps the CPU alive indefinitely. Battery Historian exposes this as a continuous partial wakelock — the most common cause of the drain pattern described.

  • Background service polling the timeline API every 30 seconds

    Polling instead of FCM push is battery-hostile by design. Each poll wakes the radio, runs the full network stack, and prevents Doze mode from engaging — even when there is nothing new to fetch.

  • AlarmManager using ELAPSED_REALTIME_WAKEUP bypassing Doze mode entirely

    ELAPSED_REALTIME_WAKEUP fires even during Doze. Combined with a network request on each alarm, it defeats Android's entire power-saving system. The correct approach is WorkManager with battery and network constraints.

Plausible But Wrong

  • Bitmap cache holding references to all decoded tweet images

    Holding Bitmaps in memory affects RAM usage, not battery. The drain described is CPU and radio wakeups during screen-off — not memory pressure.

  • Room database writes running without transaction blocks

    Unbatched Room writes add disk I/O overhead but would not produce the continuous overnight battery drain visible in Battery Historian.

  • RecyclerView not recycling ViewHolders on large timeline lists

    RecyclerView pool exhaustion causes jank and memory pressure during active scrolling — not background battery drain on a device with the screen off.

Summary

Twitter's background work had been implemented with direct AlarmManager wakeups and a persistent background service — patterns that predate Android's Doze mode introduced in Android 6. When Doze arrived, these patterns began fighting the OS. The app kept winning: acquiring WakeLocks, firing alarms, polling the API every 30 seconds — draining batteries across millions of devices silently. The modern fix is WorkManager with network and battery constraints for non-urgent work, and FCM high-priority messages for anything time-sensitive.

The Real Decision That Caused This

Using pre-Doze background patterns (WakeLock + AlarmManager + polling) that were never updated to respect Android's power management system introduced in API 23.

Lesson Hint

Chapter 4 (Networking & Real-Time) covers FCM vs polling trade-offs. Chapter 7 (Platform & Performance) covers WorkManager constraints and Doze-safe background work patterns.

Want to test yourself before reading the verdict?

Open Interactive Case in Autopsy Lab