在日常开发中,协程已成为处理异步任务的核心工具,广泛应用于:
• 网络请求调用 - 替代传统回调,实现同步式编码风格
• 复杂数据运算 - 在后台线程执行密集型计算任务
• 数据流处理 - 通过Flow构建响应式数据流
• 数据库操作 - 高效管理本地数据的读写访问
• 多任务协作 - 优雅处理并发执行与依赖关系
Kotlin
import android.os.Handler
import android.os.Looper
import androidx.annotation.Keep
import kotlinx.coroutines.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.CoroutineContext
/**
* 完整的UI线程工具类
* 提供协程和Handler两种方式在主线程执行任务
* 支持延迟执行、任务取消、生命周期管理等功能
*/
@Keep
object UIThreadUtils : CoroutineScope {
// 主线程协程作用域
private val mainScope = MainScope()
// 主线程Handler
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
// 存储可取消的任务
private val delayedTasks = ConcurrentHashMap<String, Runnable>()
override val coroutineContext: CoroutineContext
get() = mainScope.coroutineContext
// region 协程方式
/**
* 在主线程执行协程任务
* @param block 要执行的挂起函数块
* @return Job对象,可用于取消任务
*/
@Keep
fun launchOnUI(block: suspend CoroutineScope.() -> Unit): Job {
return mainScope.launch(Dispatchers.Main) {
try {
block()
} catch (e: CancellationException) {
// 协程取消是正常情况,不处理
throw e
} catch (e: Exception) {
handleException(e)
}
}
}
/**
* 在主线程执行协程任务并返回结果
* @param block 要执行的挂起函数块
* @return Deferred对象,可用于获取结果
*/
@Keep
fun <T> asyncOnUI(block: suspend CoroutineScope.() -> T): Deferred<T> {
return mainScope.async(Dispatchers.Main) {
try {
block()
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
handleException(e)
throw e
}
}
}
/**
* 延迟执行协程任务
* @param delayMillis 延迟时间(毫秒)
* @param block 要执行的挂起函数块
* @return Job对象,可用于取消任务
*/
@Keep
fun launchOnUIDelayed(delayMillis: Long, block: suspend CoroutineScope.() -> Unit): Job {
return mainScope.launch(Dispatchers.Main) {
try {
delay(delayMillis)
block()
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
handleException(e)
}
}
}
// region Handler方式
/**
* 在主线程执行任务(智能判断当前线程)
* @param block 要执行的代码块
*/
@Keep
fun runOnUI(block: () -> Unit) {
if (isOnMainThread()) {
safeExecute(block)
} else {
mainHandler.post { safeExecute(block) }
}
}
/**
* 在主线程执行任务(强制切换到主线程)
* @param block 要执行的代码块
*/
@Keep
fun postToUI(block: () -> Unit) {
mainHandler.post { safeExecute(block) }
}
/**
* 延迟执行任务
* @param delayMillis 延迟时间(毫秒)
* @param block 要执行的代码块
* @return 任务标识符,可用于取消任务
*/
@Keep
fun runOnUIDelayed(delayMillis: Long, block: () -> Unit): String {
val taskId = System.currentTimeMillis().toString() + "_" + block.hashCode()
val runnable = Runnable { safeExecute(block) }
delayedTasks[taskId] = runnable
mainHandler.postDelayed({
delayedTasks.remove(taskId)
safeExecute(block)
}, delayMillis)
return taskId
}
/**
* 延迟执行任务(带标签,便于管理)
* @param delayMillis 延迟时间(毫秒)
* @param tag 任务标签
* @param block 要执行的代码块
* @return 任务标识符
*/
@Keep
fun runOnUIDelayed(delayMillis: Long, tag: String, block: () -> Unit): String {
val taskId = "${tag}_${System.currentTimeMillis()}"
val runnable = Runnable { safeExecute(block) }
delayedTasks[taskId] = runnable
mainHandler.postDelayed({
delayedTasks.remove(taskId)
safeExecute(block)
}, delayMillis)
return taskId
}
/**
* 取消延迟任务
* @param taskId 任务标识符
*/
@Keep
fun cancelDelayedTask(taskId: String) {
val runnable = delayedTasks[taskId]
runnable?.let {
mainHandler.removeCallbacks(it)
delayedTasks.remove(taskId)
}
}
/**
* 取消指定标签的所有任务
* @param tag 任务标签
*/
@Keep
fun cancelDelayedTasksByTag(tag: String) {
val tasksToRemove = delayedTasks.keys.filter { it.startsWith("${tag}_") }
tasksToRemove.forEach { taskId ->
val runnable = delayedTasks[taskId]
runnable?.let {
mainHandler.removeCallbacks(it)
delayedTasks.remove(taskId)
}
}
}
/**
* 取消所有延迟任务
*/
@Keep
fun cancelAllDelayedTasks() {
delayedTasks.values.forEach { mainHandler.removeCallbacks(it) }
delayedTasks.clear()
}
// endregion
// region 工具方法
/**
* 判断当前是否在主线程
*/
@Keep
fun isOnMainThread(): Boolean {
return Looper.getMainLooper() == Looper.myLooper()
}
/**
* 确保在主线程执行,如果不在主线程则抛出异常
*/
@Keep
fun checkMainThread() {
if (!isOnMainThread()) {
throw IllegalStateException("This operation must be run on UI thread. Current thread: ${Thread.currentThread().name}")
}
}
/**
* 安全执行代码块,捕获异常
*/
private fun safeExecute(block: () -> Unit) {
try {
block()
} catch (e: Exception) {
handleException(e)
}
}
/**
* 异常处理
*/
private fun handleException(e: Exception) {
// 这里可以自定义异常处理逻辑,比如日志记录、崩溃上报等
e.printStackTrace()
// 示例:简单的日志输出
println("UIThreadUtils Exception: ${e.message}")
// 在实际项目中,你可以使用自己的日志系统
// Logger.e("UIThreadUtils", "Execute task error", e)
}
/**
* 清理资源
*/
@Keep
fun destroy() {
cancelAllDelayedTasks()
mainScope.cancel()
}
// endregion
}
// region 扩展函数
/**
* 在UI线程执行协程任务的扩展函数
*/
@Keep
fun CoroutineScope.launchOnUI(block: suspend CoroutineScope.() -> Unit): Job {
return UIThreadUtils.launchOnUI(block)
}
/**
* 在UI线程执行任务的扩展函数
*/
@Keep
fun (() -> Unit).runOnUI() {
UIThreadUtils.runOnUI(this)
}
/**
* 延迟执行的扩展函数
*/
@Keep
fun (() -> Unit).runOnUIDelayed(delayMillis: Long): String {
return UIThreadUtils.runOnUIDelayed(delayMillis, this)
}
使用的方式:
Kotlin
// 1. 协程方式
// 基本使用
UIThreadUtils.launchOnUI {
textView.text = "更新UI"
// 可以调用挂起函数
val result = withContext(Dispatchers.IO) { fetchData() }
updateUI(result)
}
// 获取结果
val deferred = UIThreadUtils.asyncOnUI {
processData()
}
// 在其他地方获取结果
launch {
val result = deferred.await()
}
// 延迟执行
UIThreadUtils.launchOnUIDelayed(1000) {
showToast("延迟显示")
}
// 2. Handler方式
// 智能判断线程
UIThreadUtils.runOnUI {
updateView()
}
// 强制切换到主线程
UIThreadUtils.postToUI {
refreshUI()
}
// 延迟执行(可取消)
val taskId = UIThreadUtils.runOnUIDelayed(2000, "refresh_task") {
refreshData()
}
// 取消任务
UIThreadUtils.cancelDelayedTask(taskId)
// 3. 扩展函数方式
{ updateUI() }.runOnUI()
{ showDialog() }.runOnUIDelayed(500)
coroutineScope.launchOnUI {
// 在协程作用域中直接使用
}
特点:
双重支持:同时提供协程和Handler两种方式
线程安全:智能判断当前线程,避免不必要的线程切换
任务管理:支持延迟任务的取消和管理
异常处理:统一的异常处理机制
生命周期感知:提供destroy方法清理资源
扩展函数:提供更简洁的调用方式
性能优化:避免内存泄漏,合理管理资源