MobileAgentSDK 是一个 Android 库,封装了 C++ MobileAgent SDK,提供 Kotlin/Java 友好的接口。通过 WebSocket 连接到 MobileAgent 服务器,实现智能体任务的执行、管理和监控。
SDK下载
使用方法
引入 AAR
将生成的 AAR 文件复制到项目中,并在 build.gradle 中添加:
dependencies {
implementation(files('libs/sdk-release.aar'))
}基本使用
方式一:使用 Flow(推荐)
Flow 提供了响应式编程风格,更适合 Kotlin 协程:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentEvent
import com.wuying.mobileagentsdk.MobileAgentState
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.collectLatest
// 使用 try-with-resources 自动管理生命周期
MobileAgentSdk().use { sdk ->
// 在协程中收集事件
lifecycleScope.launch {
sdk.events.collectLatest { event ->
when (event) {
is MobileAgentEvent.StateChanged -> {
when (event.state) {
MobileAgentState.CONNECTED -> {
Log.d("SDK", "已连接")
// 执行任务
sdk.executeTask("请帮我打开设置", 10)
}
MobileAgentState.DISCONNECTED -> {
Log.d("SDK", "已断开")
}
else -> {}
}
}
is MobileAgentEvent.TaskResult -> {
Log.d("SDK", "任务完成: ${event.resultText}")
}
is MobileAgentEvent.Stream -> {
Log.d("SDK", "流式数据: ${event.content}")
}
else -> {}
}
}
}
// 也可以只收集特定类型的事件
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.StateChanged>()
.collect { event ->
Log.d("SDK", "状态: ${event.state}")
}
}
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
Log.d("SDK", "任务结果: ${event.resultText}")
}
}
// 连接到服务器
sdk.connect("wss://localhost:30005/ws")
// SDK 会在 use 块结束时自动销毁
}方式二:使用传统 Callback
适合不熟悉协程的场景:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentCallback
import com.wuying.mobileagentsdk.MobileAgentState
MobileAgentSdk().use { sdk ->
// 设置回调
sdk.setCallback(object : MobileAgentCallback {
override fun onStateChanged(state: MobileAgentState) {
when (state) {
MobileAgentState.CONNECTED -> {
Log.d("SDK", "已连接")
sdk.executeTask("请帮我打开设置", 10)
}
MobileAgentState.DISCONNECTED -> {
Log.d("SDK", "已断开")
}
else -> {}
}
}
override fun onTaskResult(
taskId: String,
result: String,
resultText: String,
success: Boolean,
actualSteps: Int
) {
Log.d("SDK", "任务完成: $resultText")
}
override fun onStream(
taskId: String,
step: Int,
agent: String,
contentType: String,
content: String
) {
Log.d("SDK", "流式数据: $content")
}
// 其他回调根据需要实现
})
// 连接到服务器
sdk.connect("wss://localhost:30005/ws")
}生命周期管理
SDK 实现了 AutoCloseable 接口,支持 try-with-resources 模式:
// 推荐:自动管理生命周期
MobileAgentSdk().use { sdk ->
sdk.connect("wss://localhost:30005/ws")
// 使用 SDK...
} // 自动调用 close() 释放资源
// 或者手动管理
val sdk = MobileAgentSdk()
try {
sdk.connect("wss://localhost:30005/ws")
// 使用 SDK...
} finally {
sdk.close() // 或 sdk.destroy()
}API 参考
连接管理
connect(url: String)
通过 WebSocket URL 连接:
sdk.connect("wss://localhost:30005/ws")connectWithTicket(ticket: String)
通过 Ticket 连接(需要服务器支持):
sdk.connectWithTicket("your-ticket-here")disconnect()
断开连接:
sdk.disconnect()任务管理
executeTask(task: String, maxSteps: Int): String
执行任务,返回任务 ID:
val taskId = sdk.executeTask("请帮我打开设置应用", 10)pauseTask(taskId: String)
暂停任务:
sdk.pauseTask(taskId)resumeTask(taskId: String)
恢复任务:
sdk.resumeTask(taskId)cancelTask(taskId: String)
取消任务:
sdk.cancelTask(taskId)状态查询
getState(): MobileAgentState
获取当前连接状态:
val state = sdk.getState()
when (state) {
MobileAgentState.CONNECTED -> // 已连接
MobileAgentState.CONNECTING -> // 连接中
MobileAgentState.DISCONNECTED -> // 已断开
}Flow API
SDK 提供 events Flow,包含所有事件类型。可以根据需要使用 filterIsInstance 过滤特定事件:
// 收集所有事件
sdk.events.collect { event ->
when (event) {
is MobileAgentEvent.StateChanged -> // 处理状态变化
is MobileAgentEvent.TaskResult -> // 处理任务结果
is MobileAgentEvent.Stream -> // 处理流式数据
// ...
}
}
// 只收集特定类型的事件
sdk.events
.filterIsInstance<MobileAgentEvent.StateChanged>()
.collect { event ->
// 只处理状态变化
}事件类型:
事件类型 | 说明 |
| 连接状态变化 |
| Todo 列表更新 |
| 任务开始 |
| 步骤开始 |
| 流式数据 |
| 步骤结束 |
| 任务结果 |
回调接口
MobileAgentCallback 提供以下回调方法(所有方法都有空实现,可选择性重写):
方法 | 说明 |
| 连接状态变化 |
| Todo 列表更新 |
| 任务开始 |
| 步骤开始 |
| 流式数据 |
| 步骤结束 |
| 任务结果 |
| 原始 JSON 消息(未解析) |
数据类
MobileAgentState
连接状态枚举:
enum class MobileAgentState {
DISCONNECTED, // 未连接
CONNECTING, // 连接中
CONNECTED // 已连接
}TodoItem
Todo 列表项:
data class TodoItem(
val id: String,
val title: String,
val status: String,
val details: String
)MobileAgentEvent
事件密封类,所有事件类型:
sealed class MobileAgentEvent {
data class StateChanged(val state: MobileAgentState)
data class TodoWrite(val todos: Array<TodoItem>, val count: Int)
data class TaskStart(val taskId: String, val task: String, val maxSteps: Int)
data class StepStart(val taskId: String, val step: Int)
data class Stream(val taskId: String, val step: Int, val agent: String, val contentType: String, val content: String)
data class StepEnd(val taskId: String, val step: Int, val status: String, val summary: String)
data class TaskResult(val taskId: String, val result: String, val resultText: String, val success: Boolean, val actualSteps: Int)
}高级用法
解析"思考"内容
MobileAgent 在流式输出中会使用 <conclusion> 标签包裹 Agent 的思考过程。可以从 Stream 事件中提取这些内容。
思考内容的格式
Agent 在输出时会使用 XML 标签标记思考内容:
<conclusion>这里是 Agent 的思考过程...</conclusion>Kotlin 实现示例
以下代码演示如何在 Kotlin 中实时解析"思考"内容:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentEvent
class ConclusionParser {
private val conclusionBuffer = StringBuilder()
private var inConclusion = false
/**
* 处理流式数据,提取 conclusion 内容
*/
fun processStream(content: String): String? {
val result = StringBuilder()
for (char in content) {
if (!inConclusion) {
// 不在 conclusion 内,检查是否开始出现 <conclusion>
conclusionBuffer.append(char)
// 检查是否出现 <conclusion>
if (conclusionBuffer.length >= 12) {
if (conclusionBuffer.endsWith("<conclusion>")) {
inConclusion = true
conclusionBuffer.clear()
result.append("[思考] ")
} else if (conclusionBuffer.length > 12) {
// 保留最后 11 个字符
conclusionBuffer.delete(0, conclusionBuffer.length - 11)
}
}
} else {
// 在 conclusion 内,检查是否结束
conclusionBuffer.append(char)
// 检查是否出现 </conclusion>
if (conclusionBuffer.length >= 13) {
if (conclusionBuffer.endsWith("</conclusion>")) {
// 找到结束标签
inConclusion = false
val conclusionContent = conclusionBuffer.toString()
.dropLast(13) // 移除 </conclusion>
.trim()
if (conclusionContent.isNotEmpty()) {
result.append(conclusionContent).append("\n")
}
conclusionBuffer.clear()
continue
} else if (conclusionBuffer.length > 13) {
// 保留最后 12 个字符
conclusionBuffer.delete(0, conclusionBuffer.length - 12)
}
}
// 如果不是结束标签的一部分,立即输出
if (!couldBeEndTag(conclusionBuffer.toString())) {
result.append(char)
}
}
}
return if (result.isNotEmpty()) result.toString() else null
}
/**
* 检查缓冲区末尾是否可能是结束标签的前缀
*/
private fun couldBeEndTag(buffer: String): Boolean {
val prefixes = listOf(
"<", "</", "</c", "</co", "</con", "</conc",
"</concl", "</conclu", "</conclus", "</conclusi",
"</conclusio", "</conclusion"
)
return prefixes.any { buffer.endsWith(it) }
}
/**
* 重置解析器状态
*/
fun reset() {
conclusionBuffer.clear()
inConclusion = false
}
}
// 使用示例
MobileAgentSdk().use { sdk ->
val parser = ConclusionParser()
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.Stream>()
.collect { event ->
// 解析并输出
val output = parser.processStream(event.content)
if (output != null) {
print(output)
}
}
}
sdk.connect("wss://localhost:30005/ws")
}简化版本(完整接收后解析)
如果不需要实时流式输出,可以等待完整内容后再解析:
/**
* 从完整内容中提取所有 conclusion 内容
*/
fun extractConclusions(content: String): List<String> {
val conclusions = mutableListOf<String>()
val regex = "<conclusion>(.*?)</conclusion>".toRegex(RegexOption.DOT_MATCHES_ALL)
for (match in regex.findAll(content)) {
val conclusion = match.groupValues[1].trim()
if (conclusion.isNotEmpty()) {
conclusions.add(conclusion)
}
}
return conclusions
}
// 使用示例
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
// 从任务结果中提取思考内容
val conclusions = extractConclusions(event.resultText)
println("Agent 的思考过程:")
conclusions.forEachIndexed { index, conclusion ->
println("${index + 1}. $conclusion")
}
}
}输出示例
[思考] 用户想要打开设置应用,我需要先找到设置应用的包名和 Activity。
[思考] 通过查询系统应用列表,我找到了设置应用:com.android.settings/.Settings
[思考] 现在我将启动设置应用。
任务完成:已成功打开设置应用同时使用 Flow 和 Callback
两种方式可以同时使用,互不干扰:
MobileAgentSdk().use { sdk ->
// 使用 Flow 处理主要逻辑
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
// 处理任务结果
}
}
// 使用 Callback 记录日志
sdk.setCallback(object : MobileAgentCallback {
override fun onRawMessage(rawJson: String) {
// 记录原始消息用于调试
Log.d("SDK_RAW", rawJson)
}
})
sdk.connect("wss://localhost:30005/ws")
}错误处理
try {
MobileAgentSdk().use { sdk ->
sdk.connect("wss://localhost:30005/ws")
}
} catch (e: IllegalStateException) {
Log.e("SDK", "SDK 创建失败", e)
} catch (e: Exception) {
Log.e("SDK", "连接错误", e)
}检查 SDK 状态
val sdk = MobileAgentSdk()
// 检查是否已销毁
if (sdk.isDestroyed()) {
Log.w("SDK", "SDK 已被销毁")
}
// 检查是否有效
sdk.checkNotDestroyed() // 如果已销毁会抛出异常ProGuard 配置
如果启用了代码混淆,需要保留 SDK 的 native 方法。SDK 已包含 proguard-rules.pro 文件,通常不需要额外配置。
如需手动配置:
# MobileAgentSDK
-keep class com.wuying.mobileagentsdk.** { *; }
-keepclassmembers class com.wuying.mobileagentsdk.MobileAgentSdk {
private native <methods>;
}依赖
SDK 的运行时依赖非常精简:
dependencies {
// 唯一的运行时依赖
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}不包含任何 UI 框架(AppCompat、Material 等),保持轻量。