Kotlin 协程冻结 UI 线程

时间:2021-07-29 05:48:50

标签: android kotlin kotlin-coroutines ui-thread android-anr-dialog

我正在 IO Dispatcher 中执行 40 到 50 个协程,所有协程都在进行网络调用并保存响应 DB 或共享首选项。这会导致 UI 冻结,有时会导致 ANR(应用程序无响应)。下面是我用来并行启动每个协程的类。

class ParallelBatchExecutor {

private val errorHandler = CoroutineExceptionHandler { _, exception ->
    LSLogger.e("Exception: ${exception.message}", TAG)
}

private var totalItems: Int = 0
private var totalItemsCompleted: Int = 0
private var totalItemsFailed: Int = 0
private lateinit var parentJob: Job
private var childJobs = arrayListOf<Job>()
private val batchProgressMap = hashMapOf<String, BatchProgress>()

fun execute(
    context: Context,
    coroutineScope: CoroutineScope,
    batches: List<Batch>,
    batchProgressCallback: BatchProgressCallback
): Job {
    childJobs.clear()
    totalItems += getTotalItemsInBatches(batches)
    parentJob = coroutineScope.launch(errorHandler) {
        startExecution(context, coroutineScope, batches, batchProgressCallback)
    }
    return parentJob
}

private fun startExecution(
    context: Context,
    coroutineScope: CoroutineScope,
    batches: List<Batch>,
    batchProgressCallback: BatchProgressCallback
) {
    if (batches.isEmpty()) {
        sendProgressUpdate(batchProgressCallback)
        return
    }
    batches.forEach { batch ->
        if (coroutineScope.isActive) {
            val childJob = coroutineScope.launch(errorHandler) {
                executeBatch(context, batch, batchProgressCallback)
            }
            childJobs.add(childJob)
        }
    }
}

private fun executeBatch(
    context: Context,
    batch: Batch,
    batchProgressCallback: BatchProgressCallback
) {
    batch.execute(context) { batchProgress ->
        processBatchProgress(batchProgress, batchProgressCallback)
    }
}

@Synchronized
private fun processBatchProgress(
    batchProgress: BatchProgress,
    batchProgressCallback: BatchProgressCallback
) {
    if (batchProgress.currentItemSyncStatus) {
        totalItemsCompleted++
    } else {
        totalItemsFailed++
    }
    batchProgressMap[batchProgress.batchId] = batchProgress
    sendProgressUpdate(batchProgressCallback)
}

@Synchronized
private fun sendProgressUpdate(batchProgressCallback: BatchProgressCallback?) {
    if (batchProgressCallback == null) {
        return
    }
    batchProgressCallback.onBatchProgress(
        BatchExecutorProgress(
            Progress(
                totalItems,
                totalItemsCompleted,
                totalItemsFailed
            ), batchProgressMap
        )
    )
}

private fun getTotalItemsInBatches(dataSyncBatches: List<Batch>): Int {
    var totalItemInBatches = 0
    dataSyncBatches.forEach { batch ->
        totalItemInBatches += batch.itemCount
    }
    return totalItemInBatches
}

fun cancel() {
    cancelRunningJob(parentJob)
    cancelChildJobs()
}

fun cancelChildJobs() {
    childJobs.forEach { job ->
        cancelRunningJob(job)
    }
    childJobs.clear()
}

private fun cancelRunningJob(job: Job?) {
    try {
        if (job == null) {
            return
        }
        if (job.isActive) {
            job.cancel()
        }
    } catch (ignore: Exception) {

    }
}

companion object {
    private const val TAG = "MetaDataSyncer"
}}

我正在调用下面的执行函数

public void call(){
List<Batch> batches = new SyncBatchBuilder().getSyncBatches(this, mode);
new ParallelBatchExecutor(50).execute(this, Dispatchers.getIO(), batches, progress -> {
// here i am storing the progress to shared preference and sending the boradcast.
})
}

我不知道为什么它会冻结主线程。因为我在 IO 线程中运行所有协程。

0 个答案:

没有答案
相关问题