协程工具类

在日常开发中,协程已成为处理异步任务的核心工具,广泛应用于:

• 网络请求调用 - 替代传统回调,实现同步式编码风格

• 复杂数据运算 - 在后台线程执行密集型计算任务

• 数据流处理 - 通过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 {
    // 在协程作用域中直接使用
}

特点:

  1. 双重支持:同时提供协程和Handler两种方式

  2. 线程安全:智能判断当前线程,避免不必要的线程切换

  3. 任务管理:支持延迟任务的取消和管理

  4. 异常处理:统一的异常处理机制

  5. 生命周期感知:提供destroy方法清理资源

  6. 扩展函数:提供更简洁的调用方式

  7. 性能优化:避免内存泄漏,合理管理资源

相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone5 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui