协程启动方式优化
- 精确选择启动模式:
- launch:用于启动一个新的协程并立即开始执行,不返回结果。适用于不需要获取协程执行结果的场景,如日志记录等后台任务。例如:
launch {
logSomeInfo()
}
- async:启动一个新协程并返回一个
Deferred
对象,可用于获取协程执行结果。若在多个协程并行执行且需要获取其结果时使用。例如:
val deferredResult = async {
calculateSomeValue()
}
val result = deferredResult.await()
- runBlocking:阻塞当前线程直到其内部协程执行完毕。一般只在测试或应用程序入口等特定场景使用,避免在生产代码中滥用,因为它会阻塞主线程,影响用户体验。例如在测试函数中:
@Test
fun testSomeCoroutine() = runBlocking {
val result = async { calculateValueForTest() }.await()
assertEquals(expectedValue, result)
}
- 复用协程上下文:如果多个协程需要相同的上下文(如相同的调度器、异常处理等),可以复用上下文,减少创建开销。例如:
val myContext = Dispatchers.IO + CoroutineExceptionHandler { _, exception ->
Log.e("MyApp", "Coroutine error", exception)
}
launch(myContext) {
// 协程逻辑
}
调度器选择优化
- 根据任务类型选择调度器:
- Dispatchers.Main:用于更新UI等与主线程相关的操作。确保UI更新在主线程进行,以保证线程安全和良好的用户体验。例如:
launch(Dispatchers.Main) {
textView.text = "Updated text"
}
- Dispatchers.IO:适合执行I/O密集型任务,如文件读写、网络请求等。它使用一个线程池来处理这些任务,避免阻塞主线程。例如:
launch(Dispatchers.IO) {
val inputStream = FileInputStream("myFile.txt")
// 进行文件读取操作
}
- Dispatchers.Default:用于CPU密集型任务,如复杂的计算。它使用的线程池大小根据系统的CPU核心数动态调整。例如:
launch(Dispatchers.Default) {
val result = performComplexCalculation()
}
- 自定义调度器:如果默认调度器无法满足需求,可以自定义调度器。例如,对于一些需要特定线程数量或优先级的任务,可以创建自定义线程池并使用
newFixedThreadPoolContext
或 newSingleThreadContext
来创建调度器。
val myCustomDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
launch(myCustomDispatcher) {
// 执行任务
}
资源管理优化
- 及时释放资源:在协程中使用资源(如文件句柄、数据库连接等)时,要确保在协程结束时及时释放资源。可以使用
use
函数或 try - finally
块。例如,在文件读取时:
launch(Dispatchers.IO) {
File("myFile.txt").inputStream().use { inputStream ->
// 进行文件读取操作
}
}
- 避免资源泄漏:注意协程的生命周期与资源的生命周期匹配。如果协程被取消,相关资源也应该被正确释放。例如,在处理网络请求时,如果协程取消,要取消正在进行的请求:
val client = OkHttpClient()
val request = Request.Builder().url("http://example.com").build()
val call = client.newCall(request)
val job = launch {
try {
val response = call.execute()
// 处理响应
} catch (e: IOException) {
// 处理异常
}
}
job.invokeOnCompletion {
call.cancel()
}
- 合理复用资源:对于一些创建开销较大的资源,如数据库连接池,可以在多个协程间复用,而不是每次创建新协程时都创建新资源。例如,使用单例模式管理数据库连接池:
object DatabaseConnectionPool {
private val pool = HikariDataSource()
// 初始化连接池配置
fun getConnection(): Connection {
return pool.connection
}
}
launch(Dispatchers.IO) {
val connection = DatabaseConnectionPool.getConnection()
try {
// 执行数据库操作
} finally {
connection.close()
}
}