首页 > 解决方案 > 如何避免并行运行android Job?

问题描述

所以我有一个处理网络请求的 Android 作业。

这项工作可以通过多种方式开始,因此可以轻松并行运行,这对我不利。我想实现这一点,工作不会开始两次,或者如果它开始了,比在 try catch 块之前等待,直到第一个完成。

那么我怎样才能做到这一点,只有一个对象同时运行/运行。

我尝试添加 TAG 和 setUpdateCurrent false,但它没有做任何事情,所以当我开始两次工作时,它并行运行。之后我尝试了互斥锁,然后解锁。但它做了同样的事情。

使用互斥锁,我应该创建一个原子互斥锁,并通过 uniq 标签或 uuid 调用 lock?

好的,所以我弄清楚了我的互斥锁有什么问题,该作业每次都会创建一个新的互斥锁对象,所以它永远不会相同,也永远不会等待。

My Job:
class SendCertificatesJob @Inject constructor(
    private val sendSync: SendSync,
    private val sharedPreferences: SharedPreferences,
    private val userLogger: UserLogger,
    private val healthCheckApi: HealthCheckApi
) : Job(), CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO

    private val countDownLatch = CountDownLatch(1)
    private val mutex = Mutex()

    override fun onRunJob(params: Params): Result {

        var jobResult = Result.SUCCESS

        if (!CommonUtils.isApiEnabled(context))
            return jobResult

        val notificationHelper = NotificationHelper(context)
        var nb: NotificationCompat.Builder? = null

        if (!params.isPeriodic) {
            nb = notificationHelper.defaultNotificationBuilder.apply {
                setContentTitle(context.resources.getString(R.string.sending_certificates))
                    .setTicker(context.resources.getString(R.string.sending_certificates))
                    .setOngoing(true)
                    .setProgress(0, 0, true)
                    .setSmallIcon(android.R.drawable.stat_notify_sync)
                    .setLargeIcon(
                        BitmapFactory.decodeResource(
                            context.resources,
                            R.mipmap.ic_launcher
                        )
                    )
            }

            notificationHelper.notify(NOTIFICATION_ID, nb)
            jobCallback?.jobStart()
        }

        val failureCount = params.failureCount
        if (failureCount >= 3) {
            nb?.setOngoing(false)
                ?.setContentTitle(context.resources.getString(R.string.sending_certificates_failed))
                ?.setTicker(context.resources.getString(R.string.sending_certificates_failed))
                ?.setProgress(100, 100, false)
                ?.setSmallIcon(android.R.drawable.stat_sys_warning)
            notificationHelper.notify(NOTIFICATION_ID, nb)
            return Result.FAILURE
        }

        GlobalScope.launch(Dispatchers.IO) {
            mutex.lock()

            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) }

                val sb = StringBuilder()
                
                if (nb != null) {
                    nb.setOngoing(false)
                        .setContentTitle(context.resources.getString(R.string.sending_certificates_succeeded))
                        .setTicker(context.resources.getString(R.string.sending_certificates_succeeded))
                        .setProgress(100, 100, false)
                        .setStyle(NotificationCompat.BigTextStyle().bigText(sb.toString()))
                        .setSmallIcon(android.R.drawable.stat_notify_sync_noanim)

                    notificationHelper.notify(NOTIFICATION_ID, nb)
                    jobCallback?.jobEnd()
                }

                sharedPreferences.edit().putLong(KEY_LATEST_CERTIFICATES_SEND_DATE, Date().time)
                    .apply()
            } catch (e: Exception) {
                Timber.tag(TAG).e(e)

                if (nb != null) {
                    nb.setOngoing(false)
                        .setContentTitle(context.resources.getString(R.string.sending_certificates_failed))
                        .setTicker(context.resources.getString(R.string.sending_certificates_failed))
                        .setProgress(100, 100, false)
                        .setSmallIcon(android.R.drawable.stat_sys_warning)
                    notificationHelper.notify(NOTIFICATION_ID, nb)
                    jobCallback?.jobEnd()
                }

                jobResult = Result.RESCHEDULE
            } finally {
                countDownLatch.countDown()
                mutex.unlock()
            }
        }

        countDownLatch.await()

        return jobResult
    }

Job schedule:
                fun scheduleNowAsync(_jobCallback: JobCallback? = null) {
            jobCallback = _jobCallback
            JobRequest.Builder(TAG_NOW)
                .setExecutionWindow(1, 1)
                .setBackoffCriteria(30000, JobRequest.BackoffPolicy.LINEAR)
                .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                .setRequirementsEnforced(true)
                .setUpdateCurrent(true)
                .build()
                .scheduleAsync()
        }

        fun schedulePeriodicAsync() {
            jobCallback = null
            JobRequest.Builder(TAG)
                .setPeriodic(900000)
                .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
                .setRequirementsEnforced(true)
                .setUpdateCurrent(true)
                .build()
                .scheduleAsync()
        }

标签: androidkotlin

解决方案


我找到了解决我的问题的方法。

所以因为我使用匕首,我提供了一个单例互斥对象,并注入到工作中。当作业开始调用 mutex.lock() 时,由于互斥锁中只有一个对象,即使另一个作业开始,第二个作业也会等到第一个作业完成。


推荐阅读