当前位置: 首页 > news >正文

Android开发:基于 Kotlin 协程的设备指令控制工具类设计与实现

在安卓开发中,设备控制是一个常见的需求。本文将介绍如何使用 Kotlin 协程实现一个高效、健壮的设备指令控制工具类。该工具类支持指令队列、重试机制、状态管理等功能,并适配安卓平台,确保生命周期管理和主线程安全性。通过本文,你将学习到如何设计一个可扩展的工具类,并将其集成到安卓项目中。

库引入

build.gradle 文件中添加以下依赖:

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0" // Kotlin 协程
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0" // 安卓协程支持
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1" // ViewModel 支持
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" // Lifecycle 支持
}

完整代码

1. 工具类代码
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

/**
 * 指令发送工具类(适配安卓平台)
 *
 * @param sendCommand 发送指令的函数,返回指令是否成功
 * @param config 工具类配置
 */
class CommandSender(
    private val sendCommand: suspend (Command) -> Response,
    private val config: CommandSenderConfig = CommandSenderConfig()
) {
    // 指令队列
    private val commandChannel = Channel<Command>(capacity = Channel.UNLIMITED)

    // 指令状态流
    private val _commandStateFlow = MutableSharedFlow<CommandState>(replay = 1)
    val commandStateFlow = _commandStateFlow.asSharedFlow()

    // 协程作用域(使用 SupervisorJob)
    private val scope = CoroutineScope(config.dispatcher + SupervisorJob())

    // 是否正在运行
    private var isRunning = true

    init {
        // 启动指令处理协程
        scope.launch {
            for (command in commandChannel) {
                if (!isRunning) break // 如果工具类已关闭,停止处理
                sendCommandWithRetry(command)
            }
        }
    }

    /**
     * 添加指令到队列
     */
    fun addCommand(command: Command) {
        if (!isRunning) throw IllegalStateException("CommandSender is already closed")
        scope.launch {
            commandChannel.send(command)
        }
    }

    /**
     * 发送指令并重试
     */
    private suspend fun sendCommandWithRetry(command: Command, retryCount: Int = 0, currentDelay: Long = config.retryDelay) {
        log("Sending command: ${command.id}, retryCount: $retryCount")
        val response = try {
            sendCommand(command)
        } catch (e: Exception) {
            log("Command ${command.id} failed with exception: ${e.message}")
            Response(command.id, false)
        }

        if (response.isSuccess) {
            log("Command ${command.id} succeeded")
            _commandStateFlow.emit(CommandState.Success(command))
        } else if (retryCount < config.maxRetries - 1) {
            log("Retrying command: ${command.id}")
            delay(currentDelay)
            sendCommandWithRetry(command, retryCount + 1, currentDelay * 2) // 指数退避
        } else {
            log("Command ${command.id} failed after ${config.maxRetries} retries")
            _commandStateFlow.emit(CommandState.Failed(command))
        }
    }

    /**
     * 清理资源
     */
    fun cleanup() {
        isRunning = false
        scope.cancel("CommandSender is shutting down")
        commandChannel.close()
        log("CommandSender resources cleaned up")
    }

    /**
     * 日志记录
     */
    private fun log(message: String) {
        println("CommandSender: $message") // 替换为实际日志工具
    }
}

/**
 * 工具类配置
 */
data class CommandSenderConfig(
    val maxRetries: Int = 3,
    val retryDelay: Long = 300,
    val dispatcher: CoroutineDispatcher = Dispatchers.IO
)

/**
 * 指令数据类
 */
data class Command(val id: String, val data: String, val priority: Int = 0)

/**
 * 指令响应数据类
 */
data class Response(val commandId: String, val isSuccess: Boolean)

/**
 * 指令状态密封类
 */
sealed class CommandState {
    data class Pending(val command: Command, val retryCount: Int = 0) : CommandState()
    data class Success(val command: Command) : CommandState()
    data class Failed(val command: Command, val error: Throwable? = null) : CommandState()
    data class Timeout(val command: Command) : CommandState()
    data class Cancelled(val command: Command) : CommandState()
}

2. 在 ViewModel 中使用工具类
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch

class DeviceControlViewModel : ViewModel() {
    // 创建 CommandSender 实例
    private val commandSender = CommandSender(
        sendCommand = { command ->
            // 模拟发送指令并接收回码
            delay(100) // 模拟网络延迟
            Response(command.id, Math.random() > 0.5) // 随机返回成功或失败
        }
    )

    // 暴露指令状态流
    val commandStateFlow: SharedFlow<CommandState> = commandSender.commandStateFlow

    /**
     * 添加指令
     */
    fun addCommand(command: Command) {
        viewModelScope.launch {
            commandSender.addCommand(command)
        }
    }

    /**
     * 清理资源
     */
    override fun onCleared() {
        super.onCleared()
        commandSender.cleanup()
    }
}

3. 在 ActivityFragment 中观察状态
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect

class DeviceControlActivity : AppCompatActivity() {

    private val viewModel: DeviceControlViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 监听指令状态
        lifecycleScope.launch {
            viewModel.commandStateFlow.collect { state ->
                when (state) {
                    is CommandState.Success -> {
                        // 更新 UI:指令成功
                        println("Command ${state.command.id} succeeded")
                    }
                    is CommandState.Failed -> {
                        // 更新 UI:指令失败
                        println("Command ${state.command.id} failed")
                    }
                    else -> {}
                }
            }
        }

        // 添加指令
        viewModel.addCommand(Command("1", "Turn on"))
        viewModel.addCommand(Command("2", "Turn off"))
    }
}

总结

通过本文,我们实现了一个基于 Kotlin 协程的设备指令控制工具类。该工具类支持指令队列、重试机制、状态管理等功能,并适配安卓平台,确保生命周期管理和主线程安全性。希望这篇博客对你有所帮助!

相关文章:

  • Kong 可观测性最佳实践
  • 前端国际化-插件模式
  • Linux网站搭建(新手必看)
  • 项目启动报Error: cannot find module ‘node:path’
  • XXL-Job 处理大数据量并发任务的解决方案及底层原理
  • Java面试黄金宝典15
  • C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)
  • 2、学习Docker前置操作
  • 深度学习框架PyTorch——从入门到精通(10)PyTorch张量简介
  • 第七章:优化热点语句_《C++性能优化指南》_notes
  • Day24:队列的最大值
  • 音视频新人如何快速上手nginx-rtmp-module
  • 人工智能之数学基础:瑞利商的推广形式——广义瑞利商
  • 如何排查C++程序的CPU占用过高的问题
  • Python爬虫:Feapder 的详细使用和案例
  • 在Ubuntu系统上安装连接服务器的图形化界面工具
  • 宏基因组产品升级!污染物降解酶数据库——不只是塑料降解!
  • ubuntu20.04安装教程
  • 网络安全可以考取哪些证书?
  • openGl片段着色器的含义
  • 解放日报:人形机器人新赛道正积蓄澎湃动能
  • 耶路撒冷发生山火,以防长宣布紧急状态
  • 深交所修订创业板指数编制方案,引入ESG负面剔除机制
  • 中国海警位中国黄岩岛领海及周边区域执法巡查
  • 屠呦呦当选美国国家科学院外籍院士
  • 朝鲜新型驱逐舰“崔贤”号进行多项武器试验