Wiring Android's WorkManager to a Quantized On-Device LLM for Background Summarization A developer has published a pattern for wiring Android's WorkManager to a quantized on-device LLM, using llama.cpp via JNI, to run chunked document summarization in the background without OOM kills or Doze-mode deferrals. The approach uses parallel chunk Workers feeding a serial reduce Worker, with model tier selection driven by device memory ceilings — sub-1B INT4 or sub-1.5B INT4 for background work, and foreground service promotion for 3B models. The writeup notes that 7B models should be moved server-side rather than tuned to fit a 6 GB device. --- title: "Wiring WorkManager to On-Device LLMs for Background Summarization" published: true description: "Schedule quantized LLM inference in Android WorkManager, handle Doze-mode constraints, promote foreground services, and choose the right model tier for mid-range devices." tags: android, kotlin, architecture, mobile canonical url: https://blog.mvpfactory.co/wiring-workmanager-on-device-llm-background-summarization --- Let me show you a pattern I use when on-device AI needs to run reliably in the background. We are wiring Android's WorkManager to a quantized LLM — specifically llama.cpp via JNI — to perform chunked document summarization without OOM kills, Doze-mode deferrals, or angry users staring at a frozen UI. By the end of this tutorial you will have a chained Worker architecture that selects the right model tier, respects memory ceilings, and promotes to a foreground service only when the model demands it. 2.9+ on the classpath CoroutineWorker Here is the gotcha that will save you hours: the memory ceiling on a mid-range device determines your entire architecture. A Snapdragon 6 Gen 1 with 6 GB RAM leaves your app process roughly 1.8–2.2 GB before the OOM killer becomes aggressive. | Model Size | INT8 RAM | INT4 RAM | Safe on 6 GB device? | |---|---|---|---| | 1B | ~1.0 GB | ~0.6 GB | Both tiers | | 1.5B Phi-2 class | ~1.5 GB | ~0.9 GB | Both with headroom | | 3B | ~3.0 GB | ~1.7 GB | INT4 only | | 7B | ~7.0 GB | ~4.0 GB | Neither — move server-side | For background Workers without foreground promotion, target sub-1B INT4 or sub-1.5B INT4. If you find yourself rationalizing a 7B model on a 6 GB device, that is a signal to move inference server-side, not to keep tuning constraints. The docs do not mention this, but setRequiresBatteryNotLow is non-negotiable for inference workloads — LLM inference drains battery fast enough to trigger system-level throttling mid-run. val inferenceConstraints = Constraints.Builder .setRequiresBatteryNotLow true .setRequiredNetworkType NetworkType.NOT REQUIRED .build val summarizeRequest = OneTimeWorkRequestBuilder