Android技术问题:MainActivity无法接收Worker发送的更新
问题分析与解决方案
你的问题核心在于:WorkManager的setProgressAsync调用无法触发UI层的Observer更新,尽管AMQP消息回调已执行。这是因为传统Worker的线程阻塞机制、WorkManager任务类型的适配问题,以及Wear OS的后台限制共同导致的。
关键问题点
- 传统
Worker不适合长期运行任务:你用Object().wait()阻塞Worker线程,导致WorkManager无法及时同步进度状态到UI层。 - AMQP回调线程的上下文问题:
handleDelivery运行在AMQP客户端的独立线程中,直接调用setProgressAsync无法保证进度更新被WorkManager正确处理。 - 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
相关产品推荐
相关产品推荐

