本文通过完整示例演示如何使用Kotlin协程优雅处理Android异步任务,涵盖基础到进阶用法。所有示例均基于Android Studio环境验证,建议配合实际项目练习。
一、Android多线程的挑战
传统方案痛点分析
// 典型回调地狱示例(完整Activity代码)
class LegacyActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 模拟嵌套回调
Thread {
val userData = api.fetchUserBlocking() // 阻塞调用
handler.post {
updateUserUI(userData)
Thread {
val orderData = api.fetchOrdersBlocking(userData.id)
handler.post {
updateOrdersUI(orderData)
// 更多嵌套回调...
}
}.start()
}
}.start()
}
private fun updateUserUI(data: User) {/*...*/}
private fun updateOrdersUI(data: List<Order>) {/*...*/}
}
二、协程环境配置
完整Gradle配置
// app/build.gradle
android {
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
}
}
dependencies {
// 协程核心库
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
// Lifecycle扩展
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
// Retrofit协程支持
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
}
三、协程基础用法
完整ViewModel示例
class MainViewModel(
private val repository: DataRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState
// 完整的协程工作流
fun loadCombinedData() {
viewModelScope.launch {
try {
_uiState.value = UiState.Loading
// 并行执行三个异步操作
val userDeferred = async { repository.getUser() }
val newsDeferred = async { repository.getNews() }
val adsDeferred = async { repository.getAds() }
// 等待所有结果
val user = userDeferred.await()
val news = newsDeferred.await()
val ads = adsDeferred.await()
_uiState.value = UiState.Success(
CombinedData(user, news, ads)
)
} catch (e: Exception) {
_uiState.value = UiState.Error(e)
}
}
}
// 完整的状态封装
sealed class UiState {
object Loading : UiState()
data class Success(val data: CombinedData) : UiState()
data class Error(val exception: Exception) : UiState()
}
}
四、完整网络请求示例
Retrofit接口定义
interface ApiService {
@GET("user/{id}")
suspend fun getUser(@Path("id") userId: String): User
@GET("news")
suspend fun getNews(): List<NewsItem>
@GET("ads")
suspend fun getAds(): List<Ad>
}
// 数据类定义
data class User(
val id: String,
val name: String,
val avatarUrl: String
)
data class NewsItem(val title: String, val content: String)
data class Ad(val imageUrl: String, val link: String)
Repository实现
class DataRepository(
private val api: ApiService,
private val db: AppDatabase
) {
// 带缓存的完整数据获取
suspend fun getCachedUser(userId: String): User {
return withContext(Dispatchers.IO) {
// 优先从数据库读取
val localUser = db.userDao().getById(userId)
if (localUser != null) {
return@withContext localUser
}
// 网络请求
val remoteUser = api.getUser(userId)
// 存入数据库
db.userDao().insert(remoteUser)
remoteUser
}
}
// 带重试机制的请求
suspend fun getNewsWithRetry(retries: Int = 3): List<NewsItem> {
var currentRetry = 0
while (currentRetry < retries) {
try {
return api.getNews()
} catch (e: IOException) {
if (++currentRetry == retries) throw e
delay(1000 * currentRetry)
}
}
return emptyList()
}
}
五、复杂UI交互案例
多步骤表单提交
class FormViewModel : ViewModel() {
private val _validationState = MutableStateFlow<ValidationState>(ValidationState.Idle)
val validationState: StateFlow<ValidationState> = _validationState
fun submitForm(formData: FormData) {
viewModelScope.launch {
_validationState.value = ValidationState.Processing
try {
// 并行验证
val usernameCheck = async { validateUsername(formData.username) }
val emailCheck = async { validateEmail(formData.email) }
val passwordCheck = async { validatePassword(formData.password) }
// 等待结果
val results = awaitAll(usernameCheck, emailCheck, passwordCheck)
// 处理错误
val errors = results.filterIsInstance<ValidationResult.Error>()
if (errors.isNotEmpty()) {
_validationState.value = ValidationState.Error(
errors.map { it.reason }
)
return@launch
}
// 提交数据
val response = withContext(Dispatchers.IO) {
api.submitForm(formData)
}
_validationState.value = when {
response.isSuccess -> ValidationState.Success
else -> ValidationState.Error(listOf("Server error: ${response.code}"))
}
} catch (e: Exception) {
_validationState.value = ValidationState.Error(
listOf("Network error: ${e.message}")
)
}
}
}
// 完整的验证状态机
sealed class ValidationState {
object Idle : ValidationState()
object Processing : ValidationState()
object Success : ValidationState()
data class Error(val messages: List<String>) : ValidationState()
}
}
六、协程最佳实践
生命周期感知组件
class MainActivity : AppCompatActivity() {
// 创建Activity专属的协程作用域
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 启动协程
scope.launch {
try {
val data = withContext(Dispatchers.IO) {
repository.loadInitialData()
}
updateUI(data)
} catch (e: Exception) {
showError(e)
}
}
}
override fun onDestroy() {
super.onDestroy()
// 自动取消所有子协程
scope.cancel()
}
private fun updateUI(data: Data) {/*...*/}
private fun showError(e: Exception) {/*...*/}
}
高级并发模式
class ImageLoader(private val scope: CoroutineScope) {
// 使用Channel实现生产者-消费者模式
private val imageChannel = Channel<Bitmap>()
init {
startConsumer()
}
fun loadImage(url: String) {
scope.launch(Dispatchers.IO) {
val bitmap = downloadImage(url)
imageChannel.send(bitmap)
}
}
private fun startConsumer() {
scope.launch(Dispatchers.Main) {
for (bitmap in imageChannel) {
updateImageView(bitmap)
}
}
}
}
// 使用Mutex实现线程安全
class SafeCounter {
private var count = 0
private val mutex = Mutex()
suspend fun increment() {
mutex.withLock {
count++
}
}
}
七、调试与优化
协程调试配置
// 创建带名称的协程上下文
val debugContext = CoroutineName("NetworkRequest") + Dispatchers.IO
// 在Android Studio的Run/Debug配置中添加VM参数:
// -Dkotlinx.coroutines.debug=on
// 日志输出示例
class CoroutineLogger : CoroutineScope by CoroutineScope(SupervisorJob()) {
fun fetchData() {
launch(CoroutineName("DataFetch") + Dispatchers.IO) {
println("Coroutine ${coroutineContext[CoroutineName]} is running")
// ...
}
}
}
性能优化技巧
// 限制并发数量
suspend fun batchProcess(items: List<Item>) {
val semaphore = Semaphore(5) // 最大5个并发
coroutineScope {
items.forEach { item ->
launch(Dispatchers.IO) {
semaphore.acquire()
try {
processItem(item)
} finally {
semaphore.release()
}
}
}
}
}
// 使用Flow实现分页加载
fun pagingFlow(pageSize: Int) = flow {
var page = 0
while (true) {
val items = api.loadPage(page, pageSize)
emit(items)
if (items.size < pageSize) break
page++
}
}.flowOn(Dispatchers.IO)
八、完整项目结构
app/
├── src/
│ ├── main/
│ │ ├── java/com/example/
│ │ │ ├── di/ // 依赖注入
│ │ │ ├── data/
│ │ │ │ ├── api/ // Retrofit接口
│ │ │ │ ├── db/ // Room数据库
│ │ │ │ └── repository/ // 数据仓库
│ │ │ ├── ui/
│ │ │ │ ├── viewmodel/ // ViewModels
│ │ │ │ └── activities/ // 协程使用示例
│ │ │ └── utils/
│ │ │ └── CoroutineUtils.kt // 协程扩展方法
九、扩展工具类
// CoroutineUtils.kt
object CoroutineUtils {
// 带超时的重试机制
suspend fun <T> retryWithTimeout(
times: Int = 3,
timeout: Long = 5000,
block: suspend () -> T
): T {
var currentRetry = 0
while (true) {
try {
return withTimeout(timeout) { block() }
} catch (e: Exception) {
if (currentRetry++ >= times) throw e
delay(1000 * currentRetry)
}
}
}
// 进度报告协程
fun <T> executeWithProgress(
scope: CoroutineScope,
block: suspend (progress: (Int) -> Unit) -> T,
onProgress: (Int) -> Unit,
onComplete: (Result<T>) -> Unit
): Job {
return scope.launch {
try {
val result = block { progress ->
withContext(Dispatchers.Main) {
onProgress(progress)
}
}
onComplete(Result.success(result))
} catch (e: Exception) {
onComplete(Result.failure(e))
}
}
}
}
十、协程与其它技术结合
与WorkManager集成
class UploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
val fileUri = inputData.getString("file_uri") ?: return Result.failure()
val file = File(URI(fileUri))
withContext(Dispatchers.IO) {
repository.uploadFile(file)
}
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
与Compose集成
@Composable
fun UserProfileScreen(userId: String) {
val viewModel: UserViewModel = viewModel()
val uiState by viewModel.uiState.collectAsState()
LaunchedEffect(userId) {
viewModel.loadUser(userId)
}
when (val state = uiState) {
is UserViewModel.UserState.Loading -> LoadingSpinner()
is UserViewModel.UserState.Success -> ProfileContent(state.user)
is UserViewModel.UserState.Error -> ErrorMessage(state.message)
}
}
通过以上完整示例,我们可以看到Kotlin协程如何以结构化的方式解决Android开发中的复杂异步问题。建议读者按照以下步骤实践:
- 从简单的网络请求开始,体验线程切换
- 在ViewModel中实现数据加载逻辑
- 尝试处理多个并行任务
- 集成到现有项目中的复杂场景
- 使用调试工具分析协程执行情况
协程的学习曲线可能较陡,但一旦掌握将极大提升Android开发的效率和质量。建议配合Android Studio的协程调试功能和Kotlin Playground进行实践练习。