MobileAgentSDK Android版使用指南

更新时间:
复制为 MD 格式

MobileAgentSDK 是一个 Android 库,封装了 C++ MobileAgent SDK,提供 Kotlin/Java 友好的接口。通过 WebSocket 连接到 MobileAgent 服务器,实现智能体任务的执行、管理和监控。

SDK下载

MobileAgentSDK Android

使用方法

引入 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 ->
        // 只处理状态变化
    }

事件类型:

事件类型

说明

MobileAgentEvent.StateChanged

连接状态变化

MobileAgentEvent.TodoWrite

Todo 列表更新

MobileAgentEvent.TaskStart

任务开始

MobileAgentEvent.StepStart

步骤开始

MobileAgentEvent.Stream

流式数据

MobileAgentEvent.StepEnd

步骤结束

MobileAgentEvent.TaskResult

任务结果

回调接口

MobileAgentCallback 提供以下回调方法(所有方法都有空实现,可选择性重写):

方法

说明

onStateChanged(state: MobileAgentState)

连接状态变化

onTodoWrite(todos: Array<TodoItem>, count: Int)

Todo 列表更新

onTaskStart(taskId: String, task: String, maxSteps: Int)

任务开始

onStepStart(taskId: String, step: Int)

步骤开始

onStream(taskId, step, agent, contentType, content)

流式数据

onStepEnd(taskId, step, status, summary)

步骤结束

onTaskResult(taskId, result, resultText, success, actualSteps)

任务结果

onRawMessage(rawJson: String)

原始 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 等),保持轻量。