我正在 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 线程中运行所有协程。