androidengineers.Book a session

Battery & Network Optimization

Exercise: Cut Background Energy by 25%

exercise55 minHard

A systematic exercise to identify, measure, and eliminate background battery drains — targeting a 25% reduction in background energy consumption.

Setup: Baseline Measurement

# 1. Charge device to 100%
# 2. Disconnect charger via adb (simulates unplugged)
adb shell dumpsys battery unplug

# 3. Reset battery stats
adb shell dumpsys batterystats --reset

# 4. Launch app, use it normally for 5 minutes
# 5. Background the app — leave it for 20 minutes
# 6. Capture bugreport
adb bugreport > ~/Desktop/before_optimization.zip

# 7. Reconnect charger
adb shell dumpsys battery reset

Open before_optimization.zip in Battery Historian. Note:

  • Total battery consumed during background period
  • Top 3 sources: wakelock / network / location

Step 1: Audit Active Background Services

adb shell dumpsys activity services com.example.myapp

For each service found running in background:

  • Is it a ForegroundService? (check for notification)
  • Is it doing network work? (check against historian)
  • Can it be replaced with WorkManager?
// Replace background Service doing periodic network sync:

// ❌ Service with a Handler loop
class SyncService : Service() {
    private val handler = Handler(Looper.getMainLooper())
    override fun onStartCommand(...) {
        handler.postDelayed({ sync(); onStartCommand(...) }, 30_000)
        return START_STICKY
    }
}

// ✅ WorkManager with CONNECTED constraint
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
    .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
    .build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork("sync", ExistingPeriodicWorkPolicy.KEEP, syncRequest)

Step 2: Audit WakeLocks

# Find wakelocks held by your app
adb shell dumpsys power | grep -A5 "com.example.myapp"

In Battery Historian: look for your package in the "Wakelock in" row. Any wakelock held for > 5 seconds during background is suspicious.

// Find leaked wakelock: search codebase for:
// powerManager.newWakeLock
// Ensure every acquire has a paired release in finally

// Audit checklist:
// [ ] All WakeLocks use acquire(timeoutMs) as a safety net
// [ ] All WakeLock.release() calls are in finally blocks
// [ ] No WakeLocks held across config changes (they leak with the Activity)

Step 3: Audit Network Activity

# Network usage summary
adb shell dumpsys netstats detail | grep "com.example.myapp"

In Battery Historian: look for the mobile_radio row during your background period. Each spike is a radio wake event.

For each spike:

  1. Correlate with your WorkManager/JobScheduler schedule
  2. If more frequent than scheduled → un-batched network calls
  3. Fix: ensure all background network goes through WorkManager, not direct AsyncTask/Thread
// ❌ Unscheduled background network in BroadcastReceiver
class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Thread { api.sync() }.start()  // immediate network — no batching
    }
}

// ✅ Schedule via WorkManager — respects Doze, batching
class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        WorkManager.getInstance(context).enqueue(
            OneTimeWorkRequestBuilder<SyncWorker>()
                .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
                .build()
        )
    }
}

Step 4: Audit Location Usage

# Location access by app
adb shell dumpsys location | grep "com.example.myapp"
// Common location leak: requesting updates in onStart, forgetting to stop in onStop
class MapActivity : AppCompatActivity() {
    override fun onStart() {
        super.onStart()
        locationManager.requestLocationUpdates(provider, 1000, 10f, listener)
    }

    // ❌ Missing: onStop doesn't remove updates
    // ✅ Fix:
    override fun onStop() {
        super.onStop()
        locationManager.removeUpdates(listener)
    }
}

Step 5: After Optimization — Re-measure

adb shell dumpsys battery unplug
adb shell dumpsys batterystats --reset
# Background app for same 20 minutes
adb bugreport > ~/Desktop/after_optimization.zip
adb shell dumpsys battery reset

Compare:

  • Wakelock hold time: before vs after
  • Radio wake count: before vs after
  • Background CPU time: before vs after

Expected Results

OptimizationTypical battery saving
Replace background Service with WorkManager5–10%
Fix leaked WakeLocks2–5%
Batch network (5 calls → 1 batch)5–8%
Remove background location leak10–15%
Total22–38%

Verify with Android Vitals

After shipping the fix, check Google Play Console → Android Vitals → Battery. The "Background Battery" metric should improve in the P90 cohort within 2–3 weeks of the update reaching users.

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Exercise: Cut Background Energy by 25% | Android System Design | Android Engineers