在Kotlin中安全的管理资源

发布于:2025-08-31 ⋅ 阅读:(26) ⋅ 点赞:(0)

1. 安全管理资源的方法

安全管理资源的方法是自动释放资源。对于短生命周期资源,常规的try/finally已足够。对于长生命周期资源,需要使用RAII(Resource Acquisition Is Initialization,资源获取即初始化)。RAII将资源的生命周期与属主(对象/作用域)生命周期绑定,实现:

  • 资源在属主创建时主动获取(如打开文件、连接数据库)。
  • 资源在属主销毁时自动释放(如关闭文件、断开连接),无需开发者手动干预。

C++通过构造函数和析构函数实现RAII:栈对象离开作用域、堆对象被delete时,析构函数会自动调用,释放资源。JVM由于不支持析构函数概念,同时GC只回收对象内存,不回收业务资源(如相机设备、文件句柄),因此JVM语言通常难以实现RAII。

RAII的核心是保证资源与属主对象的生命周期一致性。JVM的困难在于无法确定生命周期边界。而Kotlin的结构化并发特性,由于提供了感知作用域生命周期的机制,恰好可以解决这个困难。当作用域结束后,协程中的对象不再有效(可能尚未回收,但对于应用程序来说,对象生命周期已经结束)。利用这个特性,就可以在Kotlin中实现RAII。

2. 短生命周期资源

短生命周期资源指存在于单个代码块内、使用后需立即释放的资源,例如:

  • 单次文件读取
  • 临时数据库查询结果

这类资源的管理方法是“即用即释放”。即使不适用RAII,这类资源也可以安全的管理。

SomeResource r;
try {
  r = allocate();
  r.doSomething();
} finally {
  r.release();
}

Kotlin为这类资源提供了use语法糖。

someClosableResource.use { r ->
    r.doSomething()
    // 离开use作用域后,自动调用r.close()释放资源
}

3. 长生命周期资源

长生命周期资源指跨越多个方法或协程、需要长期使用的资源,典型场景包括:

  • 相机对象
  • 网络连接池
  • 数据库连接池

由于这类对象的生命周期跨越多个方法或协程,不能使用“即用即释放”的策略管理,需要使用RAII策略,将资源释放代码加入到Job.invokeOnCompletion中。

class ResourceManager(private val scope: CoroutineScope) {
    private var resource: Resource? = null
    private var isReleased = false
    private val mutex = Mutex()
    private var releaseRegistration: DisposableHandle? = null

    suspend fun acquire(): Resource = mutex.withLock {
        if (isReleased) throw IllegalStateException("资源已释放")
        resource?.let { return it }

        return suspendCancellableCoroutine { cont ->
            cont.invokeOnCancellation {
                // 处理协程取消时的部分初始化
                if (resource == null) cleanupPartialAllocation()
            }

            try {
                val newResource = doAllocate()
                resource = newResource

                // 首次获取时注册释放回调
                if (releaseRegistration == null) {
                    releaseRegistration = scope.coroutineContext[Job]?.invokeOnCompletion {
                        release()
                    }
                }

                cont.resume(newResource)
            } catch (e: Exception) {
                cont.resumeWithException(IllegalStateException("分配失败", e))
            }
        }
    }

    fun withResource(block: (Resource) -> Unit) {
        if (isReleased) throw IllegalStateException("资源已释放")
        resource?.let(block) ?: throw IllegalStateException("资源未初始化")
    }

    private fun doAllocate(): Resource {
        // 实际资源分配逻辑
    }

    private fun release() {
        if (isReleased) return
        resource?.close()
        resource = null
        isReleased = true
        releaseRegistration?.dispose() // 清理回调注册
    }

    private fun cleanupPartialAllocation() {
        // 清理部分初始化的资源
    }
}

class MyViewModel : ViewModel() {
    private val resourceManager = ResourceManager(viewModelScope)


    fun foo() {
        viewModelScope.launch {
            resourceManager.acquire()
            // 使用资源
            resourceManager.withResource { res ->
                res.doSomething()
            }
        }
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到