androidengineers.Book a session

Battery & Network Optimization

Network Batching, Caching & Compression

article25 minMedium

Network radio activity is one of the biggest battery drains on mobile. Each time the radio activates from idle, it consumes significant power. Batching, caching, and compressing network requests dramatically reduces radio wake events.

The Radio Energy Problem

Mobile radios (LTE/5G) have state machines:

  • Idle (low power) → triggered to Active by any network request
  • Active → stays active for ~10–20 seconds after last packet (tail energy)
  • Returns to Idle after tail timer expires

A poorly-designed app that makes many small network calls keeps the radio permanently in "active" state, consuming 2–3× more battery than an app that batches the same requests.

Strategy 1: Request Batching

Combine multiple small requests into fewer larger ones:

// ❌ Sends 20 separate network requests for 20 events
class EventTracker {
    fun trackEvent(event: AnalyticsEvent) {
        api.track(event)  // network call per event
    }
}

// ✅ Buffer events; send in a batch every 30 seconds
class EventTracker(private val api: AnalyticsApi) {
    private val buffer = mutableListOf<AnalyticsEvent>()
    private val flushJob = CoroutineScope(Dispatchers.IO)

    init {
        flushJob.launch {
            while (isActive) {
                delay(30_000)  // wait 30 seconds
                flush()
            }
        }
    }

    @Synchronized
    fun trackEvent(event: AnalyticsEvent) {
        buffer.add(event)
        if (buffer.size >= 50) flushJob.launch { flush() }  // also flush at 50 events
    }

    @Synchronized
    private suspend fun flush() {
        if (buffer.isEmpty()) return
        val batch = buffer.toList()
        buffer.clear()
        try {
            api.trackBatch(batch)
        } catch (e: IOException) {
            buffer.addAll(0, batch)  // re-queue on failure
        }
    }
}

Strategy 2: HTTP Response Caching (OkHttp)

Covered in the HTTP Caching lesson — a properly configured Cache on OkHttp eliminates redundant network requests for unchanged data.

val client = OkHttpClient.Builder()
    .cache(Cache(File(context.cacheDir, "http"), 10L * 1024 * 1024))
    .build()

Strategy 3: GZIP / Content Compression

OkHttp sends Accept-Encoding: gzip automatically and decompresses responses. Ensure your server sends compressed responses. For JSON APIs this typically reduces payload size by 60–80%.

For request bodies (large uploads), compress manually:

fun createGzippedBody(json: String): RequestBody {
    val buffer = Buffer()
    GzipSink(buffer).buffered().use { it.writeUtf8(json) }
    return object : RequestBody() {
        override fun contentType() = "application/json; charset=utf-8".toMediaType()
        override fun writeTo(sink: BufferedSink) { sink.write(buffer, buffer.size) }
        override fun contentLength() = buffer.size
    }
}

val request = Request.Builder()
    .url(url)
    .header("Content-Encoding", "gzip")
    .post(createGzippedBody(jsonBody))
    .build()

Strategy 4: WorkManager for Background Network

Schedule non-urgent network work to run when the radio is already active for other reasons:

// Upload analytics batch only when connected and not low battery
val uploadRequest = OneTimeWorkRequestBuilder<UploadAnalyticsWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresBatteryNotLow(true)
            .build()
    )
    .build()

WorkManager.getInstance(context).enqueueUniqueWork(
    "analytics_upload",
    ExistingWorkPolicy.REPLACE,
    uploadRequest
)

Strategy 5: Protocol Buffers / Binary Formats

For high-frequency data, swap JSON for a binary format:

FormatRelative sizeRelative speed
JSON1× (baseline)
GZIP'd JSON~0.35×~1.2× (compression overhead)
Protocol Buffers~0.35×~3× faster to encode/decode
FlatBuffers~0.35×~10× faster (zero-copy)

Proto is the most common binary format for Android–backend communication. See the gRPC lesson for setup.

Battery Historian Analysis

After testing your network strategy:

# Enable full battery logging
adb shell dumpsys batterystats --reset
adb shell dumpsys batterystats --enable full-wake-history

# Use the device, then export
adb bugreport > bugreport.zip
# Drag bugreport.zip to Battery Historian (web tool)

In Battery Historian, look at the "Network" row — spiky patterns indicate un-batched requests; smooth patterns indicate good batching.

Key Takeaways

StrategyBattery impact
Request batchingHigh — fewer radio wake events
HTTP cachingHigh — eliminates repeat requests
GZIP compressionMedium — smaller payloads, faster radio
WorkManager constraintsMedium — piggybacks on existing radio sessions
Binary protocolsLow — faster, but radio time is the same

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Network Batching, Caching & Compression | Android System Design | Android Engineers