首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 進程被 Kill 時的資料上報解決方案:定時持久化 + 啟動補發

     編輯:程式設計綜合問答

核心痛點

Android 系統在記憶體不足時會直接殺死進程(onDestroy()、onStop() 均不會被呼叫),導致依賴生命週期回調的上報邏輯失效。

不可行方向分析

  • onDestroy():使用者按 Home 進入後台,系統回收時不保證呼叫。
  • onStop():Activity 切換、鎖屏皆會觸發,無法判斷「整個 App 退出」。
  • Application.onTerminate():僅模擬器執行,實機從不調用。
  • Service + START_STICKY:系統殺進程時 Service 也會被殺,重啟延遲不可控。

推薦架構:定時持久化 + 冷啟動補發

┌─────────────┐     定時/事件觸發      ┌──────────────┐
│  業務邏輯層  │ ─────────────────────▶ │  本地持久化   │  (SharedPreferences / MMKV / Room)
│  產生上報資料 │                      │  寫入待發佇列   │
└─────────────┘                        └──────┬───────┘
                                               │
                          App 進程存活時       │  後台執行緒/WorkManager
                          定期嘗試發送 ────────┘
                                               │
                          App 冷啟動時          ▼
                          讀取待發佇列 ───▶ 發送補償請求

關鍵實作細節

  1. 資料模型: 含 eventId(UUID)、payload(JSON)、createTime、retryCount。
  2. 持久化選擇: MMKV (高效能 key-value) 或 Room (結構化查詢)。避免直接用 SharedPreferences 大量寫入導致 ANR。
  3. 發送策略:
    • App 在前台:即時發送,失敗入庫。
    • App 在後台:使用 WorkManager 週期性作業(最小 15 分鐘)嘗試發送。
    • 啟動時:Application.onCreate() 中啟動一次性 Worker 清理待發佇列。
  4. 冪等性: 服務端以 eventId 去重,客戶端發送成功後刪除本地記錄。
  5. 資料保留期: 設定 TTL(如 7 天),定期清理過期未送達記錄,防止無限增長。

程式碼片段 (Kotlin + WorkManager)

// 1. 定義 Worker
class UploadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result {
        val repo = ReportRepository.getInstance(applicationContext)
        val pending = repo.getPendingReports(limit = 50)
        pending.forEach { report ->
            val success = ApiClient.upload(report.payload).await()
            if (success) repo.delete(report.id) else repo.incrementRetry(report.id)
        }
        return if (pending.isEmpty()) Result.success() else Result.retry()
    }
}

// 2. 入庫
fun saveReport(payload: String) {
    val report = Report(UUID.randomUUID().toString(), payload, System.currentTimeMillis(), 0)
    ReportRepository.getInstance(appContext).insert(report)
    // 觸發即時上傳嘗試
    WorkManager.getInstance(appContext).enqueueUniqueWork(
        "upload_reports", ExistingWorkPolicy.REPLACE,
        OneTimeWorkRequestBuilder<UploadWorker>().build()
    )
}

// 3. 啟動補發 (Application.onCreate)
WorkManager.getInstance(this).enqueueUniqueWork(
    "startup_upload", ExistingWorkPolicy.KEEP,
    OneTimeWorkRequestBuilder<UploadWorker>().setInitialDelay(10, TimeUnit.SECONDS).build()
)

替代方案對比

方案優點缺點適用場景
定時持久化+補發可靠、不依賴生命週期、支援重試有延遲、需處理冪等統計上報、日誌收集、非即時業務
前台 Service + Notification進程優先級高、較難被殺常駐通知欄干擾使用者、系統仍可能殺音樂播放、導航、即時通訊
JobScheduler / AlarmManager 定時喚醒系統級調度Android 7+ 限制多、精確度低定期同步、備份

總結

「系統殺進程時無法執行代碼」是 Android 系統設計鐵律。唯一可行之道是將「發送」與「業務產生」解耦:業務只負責「寫入本地佇列」,由獨立的背景機制(WorkManager)負責「讀取佇列並發送」。接受「有延遲、最終一致性」的現實,換取 99.9% 以上的送達率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved