全部產品
Search
文件中心

Cloud Phone:MobileAgentSDK Android版使用指南

更新時間:May 22, 2026

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")
}

不包含任何 使用者介面架構(AppCompat、Material 等),保持輕量。