如何避免Android Job并行运行,保证同一时间仅单个实例执行
问题根因
- Mutex实例不共享:每次Job被触发时Android Job框架都会创建新的
SendCertificatesJob实例,你在类内定义的private val mutex是实例成员,每个Job实例各持一把独立的锁,自然不会触发互斥等待逻辑 - 调度配置冲突:你当前定时任务用
TAG、即时任务用TAG_NOW两个不同的任务标记,setUpdateCurrent(true)仅会替换同标记的待执行任务,不同标记的任务会被独立调度,这也是并行问题的诱因之一
解决方案
1. 实现Mutex全局单例
由于你用了依赖注入框架,可以直接把Mutex定义为全局单例,所有Job实例共享同一把锁即可:
// 方案1:直接定义全局单例锁,仅用于该任务互斥,实现最简单 object SendCertificatesLock { val mutex = Mutex() } // 方案2:如果要统一通过DI注入,在注入模块中声明单例 @Module @InstallIn(SingletonComponent::class) object LockModule { @Provides @Singleton @Named("SendCertificatesMutex") fun provideSendCertificatesMutex() = Mutex() }
删除Job类中原有的实例级mutex和countDownLatch变量,改为注入/直接引用全局单例锁。
2. 简化协程执行逻辑
你原代码中用GlobalScope.launch加CountDownLatch等待的写法完全没必要,可直接用runBlocking配合Mutex的withLock扩展方法实现自动加解锁、自动等待前序任务:
override fun onRunJob(params: Params): Result { // 原有API可用性校验、通知初始化、失败次数判断逻辑保持不变 if (!CommonUtils.isApiEnabled(context)) return Result.SUCCESS // ...省略原有前置逻辑 return runBlocking(Dispatchers.IO) { // 直接加全局共享锁,前序任务未执行完时会自动挂起等待 SendCertificatesLock.mutex.withLock { var jobResult = Result.SUCCESS userLogger.writeLogToFile("SendCertificatesJob.onRunJob(), date:" + Calendar.getInstance().time) try { // 原有业务逻辑直接放在此处即可,包括测试循环、健康检查、同步逻辑 //Test var doIt = true var count =0 while (doIt){ Timber.d("SendSyncWorker: $count") count++ delay(10000) if(count == 12) doIt = false } healthCheckApi.checkHealth(ApiModule.API_KEY).await() try { sendSync.syncRecordedClients() } catch (e: Exception) { e.printStackTrace() } val result = sendSync().forEachParallel2() result.firstOrNull { it.second != null }?.let { throw Exception(it.second) } // 原有成功通知、缓存更新逻辑保持不变 // ... } catch (e: Exception) { Timber.tag(TAG).e(e) // 原有失败通知逻辑保持不变 // ... jobResult = Result.RESCHEDULE } return@runBlocking jobResult } } }
withLock扩展会自动处理异常场景的锁释放,不需要手动调用lock/unlock,避免死锁风险。
3. 调度逻辑可选优化
如果你不需要保留定时、即时任务的独立调度队列,可以把两类任务的TAG统一,配合setUpdateCurrent(true)可以直接过滤掉重复的待执行任务,减少不必要的任务排队。
兜底兼容
如果需要应对进程被杀、锁状态丢失的极端场景,可以在SharedPreferences中新增一个is_job_running标记,任务启动时先写入标记为true,任务结束(无论成功失败)时重置为false,和Mutex配合做双重校验即可。
内容的提问来源于stack exchange,提问作者Andreas1234
相关产品推荐
相关产品推荐

