Android使用WorkManager实现FCM打印场景生产者消费者模式问询
Android FCM推送串行打印的生产者/消费者模式实现问题
业务背景与核心诉求
- 运行环境:搭载热敏打印机的专用Android设备
- 业务要求:应用收到FCM推送通知后需即时触发收据打印
- 现存问题:短时间收到多条推送时,现有任务处理逻辑存在缺陷,部分推送对应的收据无法打印
- 现有打印依赖:通过调用
startActivity(...)传入携带ACTION_VIEW与指定Uri的Intent,唤醒第三方开发、我方无管控权限的外部打印服务完成全流程打印,无法修改外部服务逻辑 - 初步改造思路:采用经典生产者/消费者模式,将所有待处理的打印
Intent加入任务队列,替代原有在FCMonMessageReceived(...)回调中直接调用startActivity执行打印的逻辑,需要明确消费队列的服务实现方式,保证打印任务同步串行下发。
初始WorkManager方案实现尝试
最初选型WorkManager实现队列能力(核心诉求是支持设备重启场景下队列任务不丢失、仍能正常执行),参考官方文档编写了如下实现代码:
MyFirebaseMessagingService.kt
class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { remoteMessage.data.isNotEmpty().let { _ -> try{ val content = remoteMessage.data["content"]?.let { it1 -> Json.parseToJsonElement(it1).jsonObject } content?.let { it -> val title = it["title"]?.toString() ?: "Title example" val body = it["message"]?.toString() ?: "Msg example" val pushId = it["notificationId"]?.toString() ?: "42" val data = it["data"]?.jsonObject val intent = sendToPrinterIntent(data) sendNotification(..., intent) //startActivity(intent) //TODO 替换为生产者-消费者队列逻辑 PrintingWorker.enqueueWork(this, intent) } } catch (e: Exception){ Log.d("pushMessage", "Error in json data: $e") } } } private fun sendToPrinterIntent(data: JsonObject?): Intent { return data?.let { val body = getBody(it) val uri = "customschema://q?text=$body" return Intent(Intent.ACTION_VIEW, Uri.parse(uri)) } ?: Intent(Intent.ACTION_VIEW, Uri.parse("customschema://q?text=")) } override fun onNewToken(token: String) { Log.d("FCMtoken", "Refreshed token: $token") } private fun sendNotification( messageBody: String, messageTitle: String, pushId: Int, pendingIntent: Intent ) { ... } }
PrintingWorker.kt
class PrintingWorker(private val appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { //调用startActivity并等待执行完成 return withContext(Dispatchers.IO){ //appContext.startActivity() workerParams.inputData.keyValueMap.forEach { println("key: ${it.key} value: ${it.value}") } //setForeground() Result.success() } } override suspend fun getForegroundInfo(): ForegroundInfo { return try { ForegroundInfo(NOTIFICATION_ID,createNotification()) } catch (e: Exception) { ForegroundInfo(NOTIFICATION_ID,Notification()) //示例逻辑,可忽略 } } private fun createNotification(): Notification { return NotificationCompat... } companion object{ val TAG = "PrintingWorker" val NOTIFICATION_ID = 4242 fun enqueueWork(context: Context, workData: Intent) { val workRequest = OneTimeWorkRequest.Builder(PrintingWorker::class.java) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setInputData(workDataOf(Pair("printingIntent",workData))) .build() WorkManager.getInstance(context).enqueue(workRequest) } } }
当前该方案的卡点:不确定如何配置WorkManager实现已入队Intent的串行调度消费,需要可行的实现建议。
备选方案评估
补充说明:评估多套可行方案后,考虑改用
Room+ 前台服务(ForegroundService)的方案实现需求,具体思路为:
- FCM的
onReceiveMessage回调收到通知时,在Room数据库对应表中新增一条待打印任务记录- 启动
ForegroundService,通过Flow或类似组件消费队列记录- 每条记录对应的打印任务执行完成后删除对应表项,逐条串行处理任务
需要确认两个核心问题:
- 该Room+前台服务的方案是否比WorkManager更适配当前业务场景
- 如果该方案适用,如何正确使用
Flow(或LiveData)实现队列消费逻辑,避免出现异常执行结果
内容的提问来源于stack exchange,提问作者riccardogabellone
相关产品推荐
相关产品推荐

