0.首先整理协程的启动方法
在 Kotlin 中,启动协程的方法有多种,常用的方法包括 launch、async、和 runBlocking。每种方法有不同的用途和特性。以下是它们的详细介绍:
1. launch
 
launch 是一个启动新协程的方法,返回一个 Job 对象,表示协程的工作。它适用于启动不需要返回结果的协程。
import kotlinx.coroutines.*fun main() = runBlocking {// 启动一个新的协程val job = launch {delay(1000L) // 模拟长时间运行的任务println("Hello from launch!")}println("Waiting for job...")job.join() // 等待协程完成println("Job completed!")
}
输出示例
Waiting for job...
Hello from launch!
Job completed!
2. async
 
async 方法也用于启动新协程,但它返回一个 Deferred 对象,可以用来获取协程的结果。适用于需要返回结果的异步操作。
import kotlinx.coroutines.*fun main() = runBlocking {// 启动一个新的协程并返回结果val deferred = async {delay(1000L) // 模拟长时间运行的任务"Result from async"}println("Waiting for result...")val result = deferred.await() // 获取协程的结果println("Result: $result")
}
输出示例
Waiting for result...
Result: Result from async3. runBlocking
 
runBlocking 是一个用于启动协程的特殊函数,它会阻塞当前线程,直到协程完成。通常用于主函数和测试环境。
import kotlinx.coroutines.*fun main() = runBlocking {// 启动协程println("Starting...")// 在 runBlocking 中启动新的协程launch {delay(1000L)println("Hello from runBlocking!")}// runBlocking 会等待所有子协程完成delay(2000L)println("Done!")
}
输出示例
Starting...
Hello from runBlocking!
Done!
4. withContext
withContext 用于切换到不同的协程上下文。它是一个挂起函数,适用于需要在不同上下文中执行代码的场景,比如在 IO 线程和主线程之间切换。
import kotlinx.coroutines.*fun main() = runBlocking {println("Main thread: ${Thread.currentThread().name}")val result = withContext(Dispatchers.IO) {// 切换到 IO 线程println("IO thread: ${Thread.currentThread().name}")delay(1000L) // 模拟耗时操作"Result from IO"}println("Result: $result")
}
输出示例
Main thread: main
IO thread: DefaultDispatcher-worker-1
Result: Result from IO
5. 自定义协程上下文
你也可以自定义协程上下文,例如使用 CoroutineScope 和 Job。这是在大型应用程序中管理协程的好方法。
import kotlinx.coroutines.*fun main() {val job = Job() // 创建 Jobval scope = CoroutineScope(Dispatchers.Main + job) // 创建 CoroutineScopescope.launch {delay(1000L)println("Hello from custom scope!")}// 取消所有协程job.cancel()
}
输出示例
 总结
- launch:用于启动不需要返回结果的协程。
- async:用于启动需要返回结果的协程。
- runBlocking:用于在主函数或测试中阻塞当前线程并等待协程完成。
- withContext:用于在不同的协程上下文中切换。
- 自定义协程上下文:通过 CoroutineScope和Job管理协程的生命周期。
这些方法可以根据需求灵活选择,以便更好地管理异步操作和并发任务。
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)suspend fun connect() = suspendCancellableCoroutine<Boolean> { continuation ->scope.launch {if (getState() != State.DISCONNECTED) {continuation.resumeWithException(IllegalStateException("Invalid state for connect operation: ${getState()}"))return@launch}setState(State.CONNECTING)continuation.invokeOnCancellation {bluetoothGatt?.disconnect()}bluetoothGatt = device.device.connectGatt(context, false, gattCallback {Log.v(LOG_TAG, "Device connected ${device.manufacturerData.serialNumber}")continuation.resume(true)})}}
代码是一个挂起函数 connect 的实现,使用了 suspendCancellableCoroutine 来处理 Bluetooth GATT(通用属性配置文件)设备的连接操作。
1、@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
这是一个注解,表示该函数需要 BLUETOOTH_CONNECT 权限。这是 Android 中的一种权限管理方式,确保在调用此函数时,应用程序已经获得了适当的权限。
2、suspend fun connect()
声明了一个挂起函数 connect,意味着可以在协程中调用。
挂起函数不会阻塞线程,而是可以在需要时挂起执行,允许其他协程运行。当条件满足时,挂起的函数会继续执行。
import kotlinx.coroutines.*suspend fun fetchData(): String {delay(2000L) // 模拟网络请求的延迟return "Data fetched"
}fun main() = runBlocking {println("Fetching data...")val result = fetchData() // 调用挂起函数println(result) // 打印结果
}
Fetching data...
// 等待 2 秒
Data fetched
3、suspendCancellableCoroutine<Boolean> { continuation -> ... }
使用 suspendCancellableCoroutine 创建一个可以挂起的协程,同时允许在连接操作期间取消。
continuation 是用于恢复协程执行的对象。
4、scope.launch { ... }
在一个新的协程作用域中启动异步任务。
5、状态检查
if (getState() != State.DISCONNECTED) {continuation.resumeWithException(IllegalStateException("Invalid state for connect operation: ${getState()}"))return@launch
}
该部分检查设备的当前状态。如果设备未处于 DISCONNECTED 状态,则抛出异常并恢复协程的执行,返回错误信息。
6、设置连接状态
setState(State.CONNECTING)
将设备状态设置为 CONNECTING,指示正在进行连接操作。
7、取消处理
continuation.invokeOnCancellation {bluetoothGatt?.disconnect()
}
如果协程被取消,执行此代码以断开与设备的连接。
8、连接 GATT
bluetoothGatt = device.device.connectGatt(context, false, gattCallback { ... })
使用 connectGatt 方法连接到 Bluetooth GATT 设备。这里的 gattCallback 是一个回调函数,用于处理连接结果。
9、连接成功处理
Log.v(LOG_TAG, "Device connected ${device.manufacturerData.serialNumber}")
continuation.resume(true)
连接成功后,记录日志并通过 continuation.resume(true) 恢复协程,表示连接操作成功
- 整体流程: 该函数通过检查设备状态,尝试连接到 Bluetooth GATT 设备,处理连接期间的取消,以及连接成功后的操作。
- 可读性与可维护性: 使用挂起函数和协程提高了代码的可读性和可维护性,避免了回调地狱的问题。
- 权限管理: 通过 @RequiresPermission注解确保在进行 Bluetooth 操作之前,应用程序具有必要的权限,这提高了代码的安全性。
错误处理与扩展
- 错误处理: 可以扩展对 connectGatt调用的错误处理,以便在连接失败时执行某些逻辑。
- 状态管理: 对 State的管理可以进一步优化,确保在不同状态之间转换时的健壮性。
在 Kotlin 协程中,continuation 是一个重要的概念,它允许你控制协程的执行流。具体来说,continuation 表示协程的执行上下文,可以在异步操作完成时恢复协程的执行。
continuation 的基本功能
 
-  恢复协程: continuation提供了两个主要方法,用于恢复协程的执行:- resume(value: T):用于恢复协程并返回一个值,表示操作成功。
- resumeWithException(exception: Throwable):用于恢复协程并抛出一个异常,表示操作失败。
 
-  可取消性: continuation是可取消的,这意味着如果协程在执行过程中被取消,可以在取消时处理特定逻辑。
import kotlinx.coroutines.*
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithExceptionsuspend fun fetchData() = suspendCancellableCoroutine<String> { continuation ->// 模拟异步操作,例如网络请求// 使用 CoroutineScope 启动一个新的协程GlobalScope.launch {try {// 模拟成功获取数据val data = "Fetched Data"// 恢复协程并返回数据continuation.resume(data)} catch (e: Exception) {// 恢复协程并抛出异常continuation.resumeWithException(e)}}// 处理取消continuation.invokeOnCancellation {// 执行清理工作,例如取消网络请求println("Operation was cancelled")}
}fun main() = runBlocking {try {val result = fetchData()println("Result: $result")} catch (e: Exception) {println("Error: ${e.message}")}
}
关键点
- 异步操作: continuation允许你将异步操作与协程结合,使得代码看起来更加线性和易于理解。
- 错误处理: 通过 resumeWithException可以优雅地处理错误,使得调用方能够捕获和处理异常。
- 取消处理: 使用 invokeOnCancellation可以在协程被取消时执行清理操作,例如释放资源或中止网络请求。
应用场景
- 异步编程: continuation通常用于处理需要等待结果的异步操作,例如网络请求、数据库查询等。
- 资源管理: 在涉及资源(如文件、网络连接等)的操作时,可以使用 continuation来确保在取消或出错时正确释放这些资源。
- 库开发: 当你在开发库或框架时,可以使用 continuation来提供更灵活的异步操作支持,使得调用者可以轻松使用你的 API。