You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Kotlin协程实现多独立无限循环并解决应用卡顿

问题分析与解决方案

现有代码的核心问题

  1. 主线程阻塞:viewModelScope默认使用主线程,GPS获取、数据库插入这些耗时操作在主线程执行,四次启动后主线程负载过载,直接导致卡顿。
  2. 协程无独立管理:每次调用方法都会启动新协程,没有记录每个乘客对应的协程实例,可能重复启动多个协程处理同一乘客,加剧资源消耗。
  3. 定位资源浪费:每个协程都创建新的LocationGpsTracker和LocationGpsTrackerNetwork,多实例同时监听定位会消耗大量系统资源,甚至导致定位服务异常。
  4. LiveData更新异常:复用同一个Way对象修改后postValue,LiveData可能因对象引用未变而不触发UI更新,同时多协程操作同一对象存在线程安全隐患。
  5. 空循环占用CPU:当开关关闭时,循环会无延迟空转,持续消耗CPU资源。

针对性解决方案

1. 独立管理每个乘客的协程生命周期

用Map存储每个乘客ID对应的协程Job,启动前先取消该乘客已有的协程,避免重复启动:

// ViewModel中新增协程管理容器
private val passengerJobs = mutableMapOf<Int, Job>()

// 拆分启动/停止方法,明确职责
fun startLocationTracking(context: Context, tripId: Long, passengerId: Int) {
    // 先取消该乘客已在运行的协程
    passengerJobs[passengerId]?.cancel()
    // 启动新协程并切换到IO线程执行耗时操作
    val job = viewModelScope.launch(Dispatchers.IO) {
        // 后续逻辑...
    }
    passengerJobs[passengerId] = job
}

fun stopLocationTracking(passengerId: Int) {
    passengerJobs[passengerId]?.cancel()
    passengerJobs.remove(passengerId)
}

2. 优化定位资源的创建与释放

每个协程初始化一次定位Tracker,协程取消时主动释放资源,避免资源泄漏:

// 在协程内部初始化Tracker,finally块中释放
val gpsTracker = LocationGpsTracker(context, 0, 0)
val netGpsTracker = LocationGpsTrackerNetwork(context)
try {
    // 循环逻辑...
} finally {
    // 根据Tracker的API实现停止监听、释放资源
    gpsTracker.stopTracking()
    netGpsTracker.stopTracking()
}

3. 耗时操作切换到IO线程,优化循环逻辑

将GPS获取、数据库插入放到IO线程执行,开关关闭时增加延迟,避免空循环占用CPU:

viewModelScope.launch(Dispatchers.IO) {
    while (isActive) {
        val switchState = _passengerSwitch.value?.get(passengerId - 1) ?: false
        if (switchState) {
            // 执行GPS获取、数据库插入等操作
            delay(2000)
        } else {
            // 开关关闭时降低检查频率,减少CPU消耗
            delay(500)
        }
    }
}

4. 修复LiveData更新问题

每次循环创建新的Way对象,确保LiveData能感知到数据变化;更新LiveData时切换回主线程:

// 每次循环创建新的Way实例
val currentWay = Way().apply {
    latitude2 = previousWay.latitude1
    longitude2 = previousWay.longitude1
    // ... 其他属性赋值
    latitude1 = gpsTracker.getLatitude()
    longitude1 = gpsTracker.getLongitude()
    // ... 其他属性赋值
}
// 切换到主线程更新LiveData
withContext(Dispatchers.Main) {
    _way.value = currentWay
}

5. 数据库操作异步化

确保数据库插入是挂起函数(比如Room的@Insert suspend),避免阻塞线程:

// Room Repository中定义挂起插入方法
suspend fun insertLocation(tripDetails: TripDetails) {
    locationDao.insert(tripDetails)
}

完整修改后的代码示例

private val passengerJobs = mutableMapOf<Int, Job>()
private val _way = MutableLiveData<Way>()
val way: LiveData<Way> = _way

fun startLocationTracking(context: Context, tripId: Long, passengerId: Int) {
    passengerJobs[passengerId]?.cancel()
    val job = viewModelScope.launch(Dispatchers.IO) {
        val gpsTracker = LocationGpsTracker(context, 0, 0)
        val netGpsTracker = LocationGpsTrackerNetwork(context)
        // 初始化初始位置
        var previousWay = Way().apply {
            latitude1 = gpsTracker.getLatitude()
            longitude1 = gpsTracker.getLongitude()
            accuracy1 = gpsTracker.getAccuracy()
            latitudeNet1 = netGpsTracker.getLatitude()
            longitudeNet1 = netGpsTracker.getLongitude()
            accuracyNet1 = netGpsTracker.getAccuracy()
        }
        // 初始值同步到LiveData
        withContext(Dispatchers.Main) {
            _way.value = previousWay
        }

        try {
            while (isActive) {
                val switchState = _passengerSwitch.value?.get(passengerId - 1) ?: false
                if (switchState) {
                    val currentWay = Way().apply {
                        // 复制上一次的位置数据
                        latitude2 = previousWay.latitude1
                        longitude2 = previousWay.longitude1
                        accuracy2 = previousWay.accuracy1
                        latitudeNet2 = previousWay.latitudeNet1
                        longitudeNet2 = previousWay.longitudeNet1
                        accuracyNet2 = previousWay.accuracyNet1
                        // 获取最新位置数据
                        latitude1 = gpsTracker.getLatitude()
                        longitude1 = gpsTracker.getLongitude()
                        accuracy1 = gpsTracker.getAccuracy()
                        latitudeNet1 = netGpsTracker.getLatitude()
                        longitudeNet1 = netGpsTracker.getLongitude()
                        accuracyNet1 = netGpsTracker.getAccuracy()
                    }
                    // 更新LiveData
                    withContext(Dispatchers.Main) {
                        _way.value = currentWay
                    }
                    // 插入数据库
                    val tripDetails = TripDetails(
                        latitude1 = currentWay.latitude1,
                        latitude2 = currentWay.latitude2,
                        longitude1 = currentWay.longitude1,
                        longitude2 = currentWay.longitude2,
                        accuracy1 = currentWay.accuracy1,
                        accuracy2 = currentWay.accuracy2,
                        latitudeNet1 = currentWay.latitudeNet1,
                        latitudeNet2 = currentWay.latitudeNet2,
                        longitudeNet1 = currentWay.longitudeNet1,
                        longitudeNet2 = currentWay.longitudeNet2,
                        accuracy1Net = currentWay.accuracyNet1,
                        accuracy2Net = currentWay.accuracyNet2,
                        distance = null,
                        isCalculated = false,
                        tripId = tripId.toInt(),
                        isEnded = false
                    )
                    localRepository.insertLocation(tripDetails)
                    previousWay = currentWay
                    delay(2000)
                } else {
                    delay(500)
                }
            }
        } finally {
            // 释放定位资源
            gpsTracker.stopTracking()
            netGpsTracker.stopTracking()
        }
    }
    passengerJobs[passengerId] = job
}

fun stopLocationTracking(passengerId: Int) {
    passengerJobs[passengerId]?.cancel()
    passengerJobs.remove(passengerId)
}

内容的提问来源于stack exchange,提问作者Hooman Hooshyar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 21:01:41