You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android技术问题:MainActivity无法接收Worker发送的更新

问题分析与解决方案

你的问题核心在于:WorkManager的setProgressAsync调用无法触发UI层的Observer更新,尽管AMQP消息回调已执行。这是因为传统Worker的线程阻塞机制、WorkManager任务类型的适配问题,以及Wear OS的后台限制共同导致的。

关键问题点

  1. 传统Worker不适合长期运行任务:你用Object().wait()阻塞Worker线程,导致WorkManager无法及时同步进度状态到UI层。
  2. AMQP回调线程的上下文问题:handleDelivery运行在AMQP客户端的独立线程中,直接调用setProgressAsync无法保证进度更新被WorkManager正确处理。
  3. Wear OS后台限制:未设置前台通知的长期后台任务容易被系统回收,导致Worker提前终止。

解决方案:改用CoroutineWorker实现

将Worker替换为CoroutineWorker,利用协程管理异步操作,同时适配Wear OS的后台运行规则:

1. 修改CommunicationWorker.kt

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import com.rabbitmq.client.*
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay

class CommunicationWorker(appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) {
    companion object {
        const val Progress: String = "MyProgressId"
        private const val NOTIFICATION_CHANNEL_ID = "AMQP_LISTENER_CHANNEL"
        private const val NOTIFICATION_ID = 1001
    }

    override suspend fun doWork(): Result {
        // 创建前台通知,避免Wear OS杀死后台任务
        createNotificationChannel()
        setForeground(ForegroundInfo(NOTIFICATION_ID, buildForegroundNotification()))

        val factory = ConnectionFactory().apply {
            host = "192.168.1.32"
            username = "admin"
            password = "admin"
        }

        val connection = factory.newConnection()
        val channel = connection.createChannel()
        val deferred = CompletableDeferred<Result>()

        val consumer = object : DefaultConsumer(channel) {
            override fun handleDelivery(consumerTag: String, envelope: Envelope, properties: AMQP.BasicProperties, body: ByteArray) {
                // 切换到CoroutineWorker的协程上下文更新进度
                coroutineScope {
                    setProgress(Data.Builder().putInt(Progress, 44).build())
                }
            }
        }

        channel.basicConsume("epi.tab", true, consumer)

        // 保持Worker运行,直到任务被取消
        try {
            deferred.await()
        } catch (e: CancellationException) {
            // 取消时清理资源
            channel.close()
            connection.close()
        }

        return Result.success()
    }

    private fun buildForegroundNotification(): Notification {
        return NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID)
            .setContentTitle("AMQP消息监听中")
            .setSmallIcon(R.drawable.ic_notification) // 替换为你的通知图标
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "AMQP监听通知",
                NotificationManager.IMPORTANCE_LOW
            )
            val manager = applicationContext.getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
}

2. 优化MainActivity.kt的Observer逻辑

避免重复弹出Toast,仅在进度值变化时更新UI:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val workManager = WorkManager.getInstance(applicationContext)
        val communicationWorkRequest = OneTimeWorkRequestBuilder<CommunicationWorker>().build()

        workManager.enqueue(communicationWorkRequest)
        
        var lastReceivedValue = 0
        workManager.getWorkInfoByIdLiveData(communicationWorkRequest.id)
            .observe(this) { workInfo ->
                workInfo?.let {
                    val currentValue = it.progress.getInt(CommunicationWorker.Progress, 0)
                    if (currentValue != lastReceivedValue) {
                        lastReceivedValue = currentValue
                        Toast.makeText(this, "Value received: $currentValue", Toast.LENGTH_LONG).show()
                    }
                }
            }

        setContent {
            WearApp("Android")
        }
    }
}

3. 配置权限

在AndroidManifest.xml中添加前台服务权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

为什么这样修改有效

  • CoroutineWorker避免线程阻塞:协程机制不会阻塞Worker线程,WorkManager能及时同步进度更新到UI层。
  • 前台通知适配Wear OS:Wear OS对后台任务限制严格,前台通知可让Worker保持运行状态。
  • 协程上下文切换:在AMQP回调中通过coroutineScope切换到Worker的协程上下文,确保进度更新被WorkManager正确处理。

内容的提问来源于stack exchange,提问作者alextekeuf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 05:13:25