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() } } } }