示例:网络请求管理器

import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.CopyOnWriteArrayList

class NetworkRequestManager {
    // 原子引用:线程安全地存储当前准备发送的请求ID
    private val mReadyRequestId by lazy { AtomicReference<String>() }

    // 线程安全的集合:存储所有活跃的网络请求Job
    private val mRequestJobs by lazy { CopyOnWriteArrayList<Job>() }

    // 扩展函数:跟踪Job到集合中
    fun Job.trackRequestJob(requestId: String): Job {
        mReadyRequestId.set(requestId) // 设置当前请求ID
        mRequestJobs.add(this) // 添加到集合
        invokeOnCompletion { job ->
            mRequestJobs.remove(this) // Job完成后从集合移除
            if (isCompletedConnectJobs()) {
                mReadyRequestId.set(null) // 所有请求完成后清理ID
            }
        }
        return this
    }

    // 判断所有请求是否完成
    fun isCompletedRequestJobs(): Boolean {
        return mRequestJobs.isEmpty() || mRequestJobs.all { it.isCompleted }
    }

    // 模拟发起网络请求
    suspend fun sendRequest(requestId: String) {
        val job = GlobalScope.launch {
            // 模拟网络请求
            delay(1000)
            println("Request $requestId completed")
        }
        job.trackRequestJob(requestId) // 跟踪Job
    }
}

// 使用示例
fun main() = runBlocking {
    val manager = NetworkRequestManager()
    manager.sendRequest("req1")
    manager.sendRequest("req2")

    while (!manager.isCompletedRequestJobs()) {
        delay(100)
    }
    println("All requests completed")
}

1. 线程安全的集合使用(CopyOnWriteArrayList)

  • 问题:在多线程环境下,如果多个协程同时添加或移除 Job,普通 ArrayList 会抛出 ConcurrentModificationException
  • 解决方案:使用 CopyOnWriteArrayList,它在写入时复制底层数组,读操作无需加锁,适合读多写少的场景。
  • 代码体现
  private val mRequestJobs by lazy { CopyOnWriteArrayList<Job>() }
  • 实际意义:确保 mRequestJobs 的操作(如 addremove)在多线程环境下是安全的。

2. 原子引用(AtomicReference)的应用

  • 问题:如果多个线程同时修改 mReadyRequestId,可能导致数据不一致。
  • 解决方案:使用 AtomicReference,通过原子操作保证状态修改的线程安全。
  • 代码体现
  private val mReadyRequestId by lazy { AtomicReference<String>() }
  • 实际意义:确保 mReadyRequestId 的读写操作是原子的,避免竞态条件。

3. 协程生命周期管理(Job.invokeOnCompletion)

  • 问题:需要知道每个网络请求协程何时完成,以便清理资源。
  • 解决方案:通过 invokeOnCompletion 监听 Job 的完成事件,并在完成后从集合中移除。
  • 代码体现
  invokeOnCompletion { job ->
      mRequestJobs.remove(this)
      if (isCompletedRequestJobs()) {
          mReadyRequestId.set(null)
      }
  }
  • 实际意义:自动清理已完成任务的 Job,避免内存泄漏。

4. 集合状态统一判断(isCompletedRequestJobs)

  • 问题:需要统一判断所有网络请求是否完成。
  • 解决方案:通过检查集合是否为空或所有 Job 是否完成,返回整体状态。
  • 代码体现
  fun isCompletedRequestJobs(): Boolean {
      return mRequestJobs.isEmpty() || mRequestJobs.all { it.isCompleted }
  }
  • 实际意义:提供一种高效的方式来统一管理多个任务的状态。

5. 扩展函数封装(Job.trackRequestJob)

  • 问题:重复编写 Job 的跟踪逻辑(如添加到集合、监听完成事件)。
  • 解决方案:通过扩展函数封装逻辑,提升代码复用性。
  • 代码体现
  fun Job.trackRequestJob(requestId: String): Job {
      // ...逻辑
  }
  • 实际意义:减少重复代码,提高开发效率。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐