All Products
Search
Document Center

Cloud Phone:MobileAgentSDK for Android

Last Updated:May 22, 2026

MobileAgentSDK adalah pustaka Android yang membungkus MobileAgent SDK berbasis C++ dan menyediakan antarmuka yang ramah Kotlin/Java. SDK ini terhubung ke server MobileAgent melalui WebSocket untuk mengeksekusi, mengelola, dan memantau tugas agen.

Unduh SDK

MobileAgentSDK for Android

Penggunaan

Impor AAR

Salin file AAR yang dihasilkan ke dalam proyek Anda dan tambahkan dependensi berikut ke file build.gradle Anda:

dependencies {
    implementation(files('libs/sdk-release.aar'))
}

Penggunaan dasar

Opsi 1: Gunakan Flow (disarankan)

Flow menyediakan gaya pemrograman reaktif yang sangat cocok untuk korutin 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

// Gunakan try-with-resources untuk mengelola siklus hidup secara otomatis.
MobileAgentSdk().use { sdk ->
    // Kumpulkan event dalam korutin.
    lifecycleScope.launch {
        sdk.events.collectLatest { event ->
            when (event) {
                is MobileAgentEvent.StateChanged -> {
                    when (event.state) {
                        MobileAgentState.CONNECTED -> {
                            Log.d("SDK", "Connected")
                            // Eksekusi tugas.
                            sdk.executeTask("Please help me open settings", 10)
                        }
                        MobileAgentState.DISCONNECTED -> {
                            Log.d("SDK", "Disconnected")
                        }
                        else -> {}
                    }
                }
                is MobileAgentEvent.TaskResult -> {
                    Log.d("SDK", "Task complete: ${event.resultText}")
                }
                is MobileAgentEvent.Stream -> {
                    Log.d("SDK", "Stream data: ${event.content}")
                }
                else -> {}
            }
        }
    }

    // Anda juga dapat mengumpulkan hanya jenis event tertentu.
    lifecycleScope.launch {
        sdk.events
            .filterIsInstance<MobileAgentEvent.StateChanged>()
            .collect { event ->
                Log.d("SDK", "State: ${event.state}")
            }
    }

    lifecycleScope.launch {
        sdk.events
            .filterIsInstance<MobileAgentEvent.TaskResult>()
            .collect { event ->
                Log.d("SDK", "Task result: ${event.resultText}")
            }
    }

    // Terhubung ke server.
    sdk.connect("wss://localhost:30005/ws")

    // SDK secara otomatis dihapus di akhir blok use.
}

Opsi 2: Gunakan callback

Pendekatan ini ditujukan untuk proyek yang tidak menggunakan korutin:

import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentCallback
import com.wuying.mobileagentsdk.MobileAgentState

MobileAgentSdk().use { sdk ->
    // Atur callback.
    sdk.setCallback(object : MobileAgentCallback {
        override fun onStateChanged(state: MobileAgentState) {
            when (state) {
                MobileAgentState.CONNECTED -> {
                    Log.d("SDK", "Connected")
                    sdk.executeTask("Please help me open settings", 10)
                }
                MobileAgentState.DISCONNECTED -> {
                    Log.d("SDK", "Disconnected")
                }
                else -> {}
            }
        }

        override fun onTaskResult(
            taskId: String,
            result: String,
            resultText: String,
            success: Boolean,
            actualSteps: Int
        ) {
            Log.d("SDK", "Task complete: $resultText")
        }

        override fun onStream(
            taskId: String,
            step: Int,
            agent: String,
            contentType: String,
            content: String
        ) {
            Log.d("SDK", "Stream data: $content")
        }

        // Implementasikan callback lain sesuai kebutuhan.
    })

    // Terhubung ke server.
    sdk.connect("wss://localhost:30005/ws")
}

Manajemen siklus hidup

SDK mengimplementasikan antarmuka AutoCloseable, yang mendukung pola try-with-resources:

// Disarankan: Kelola siklus hidup secara otomatis.
MobileAgentSdk().use { sdk ->
    sdk.connect("wss://localhost:30005/ws")
    // Gunakan SDK...
} // Secara otomatis memanggil close() untuk melepas sumber daya.

// Atau, kelola siklus hidup secara manual.
val sdk = MobileAgentSdk()
try {
    sdk.connect("wss://localhost:30005/ws")
    // Gunakan SDK...
} finally {
    sdk.close()  // atau sdk.destroy()
}

API

Manajemen koneksi

connect(url: String)

Terhubung ke server menggunakan URL WebSocket:

sdk.connect("wss://localhost:30005/ws")

connectWithTicket(ticket: String)

Terhubung menggunakan tiket (memerlukan dukungan dari sisi server):

sdk.connectWithTicket("your-ticket-here")

disconnect()

Putuskan koneksi dari server:

sdk.disconnect()

Manajemen tugas

executeTask(task: String, maxSteps: Int): String

Eksekusi tugas dan kembalikan ID tugas:

val taskId = sdk.executeTask("Please help me open the settings app", 10)

pauseTask(taskId: String)

Jeda tugas:

sdk.pauseTask(taskId)

resumeTask(taskId: String)

Lanjutkan tugas:

sdk.resumeTask(taskId)

cancelTask(taskId: String)

Batalkan tugas:

sdk.cancelTask(taskId)

Kueri status

getState(): MobileAgentState

Ambil status koneksi saat ini:

val state = sdk.getState()
when (state) {
    MobileAgentState.CONNECTED -> // Connected
    MobileAgentState.CONNECTING -> // Connecting
    MobileAgentState.DISCONNECTED -> // Disconnected
}

API Flow

SDK menyediakan Flow events yang mengeluarkan semua jenis event. Anda dapat menggunakan filterIsInstance untuk memfilter event tertentu:

// Kumpulkan semua event.
sdk.events.collect { event ->
    when (event) {
        is MobileAgentEvent.StateChanged -> // Tangani perubahan status.
        is MobileAgentEvent.TaskResult -> // Tangani hasil tugas.
        is MobileAgentEvent.Stream -> // Tangani data aliran.
        // ...
    }
}

// Kumpulkan hanya jenis event tertentu.
sdk.events
    .filterIsInstance<MobileAgentEvent.StateChanged>()
    .collect { event ->
        // Tangani hanya perubahan status.
    }

Jenis event:

Jenis event

Deskripsi

MobileAgentEvent.StateChanged

Status koneksi berubah.

MobileAgentEvent.TodoWrite

Daftar to-do diperbarui.

MobileAgentEvent.TaskStart

Tugas dimulai.

MobileAgentEvent.StepStart

Langkah dimulai.

MobileAgentEvent.Stream

Data aliran diterima.

MobileAgentEvent.StepEnd

Langkah selesai.

MobileAgentEvent.TaskResult

Hasil tugas diterima.

API Callback

Antarmuka MobileAgentCallback menyediakan metode callback berikut. Semua metode memiliki implementasi kosong bawaan, sehingga Anda hanya perlu meng-override yang diperlukan:

Metode

Deskripsi

onStateChanged(state: MobileAgentState)

Status koneksi berubah

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

Daftar to-do diperbarui

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

Tugas dimulai

onStepStart(taskId: String, step: Int)

Langkah dimulai

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

Data aliran diterima

onStepEnd(taskId, step, status, summary)

Langkah selesai

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

Hasil tugas diterima

onRawMessage(rawJson: String)

Pesan JSON mentah yang belum diurai.

Kelas data

MobileAgentState

Enum untuk status koneksi:

enum class MobileAgentState {
    DISCONNECTED,  // Disconnected
    CONNECTING,    // Connecting
    CONNECTED      // Connected
}

TodoItem

Kelas data untuk item to-do:

data class TodoItem(
    val id: String,
    val title: String,
    val status: String,
    val details: String
)

MobileAgentEvent

Kelas sealed yang merepresentasikan semua jenis event yang mungkin:

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

Penggunaan lanjutan

Mengurai pikiran agen

MobileAgent menggunakan tag <conclusion> dalam output alirannya untuk membungkus proses berpikir agen. Anda dapat mengekstrak konten ini dari event Stream.

Format <conclusion>

Agen menggunakan tag XML untuk menandai proses berpikirnya dalam output:

<conclusion>This is the agent's thought process...</conclusion>

Implementasi Kotlin

Kode berikut menunjukkan cara mengurai konten pikiran saat diterima:

import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentEvent

class ConclusionParser {
    private val conclusionBuffer = StringBuilder()
    private var inConclusion = false

    /**
     * Memproses data aliran untuk mengekstrak konten dari tag <conclusion>.
     */
    fun processStream(content: String): String? {
        val result = StringBuilder()

        for (char in content) {
            if (!inConclusion) {
                // Tidak berada di dalam kesimpulan; periksa awal <conclusion>.
                conclusionBuffer.append(char)

                // Periksa <conclusion>.
                if (conclusionBuffer.length >= 12) {
                    if (conclusionBuffer.endsWith("<conclusion>")) {
                        inConclusion = true
                        conclusionBuffer.clear()
                        result.append("[Thought] ")
                    } else if (conclusionBuffer.length > 12) {
                        // Simpan 11 karakter terakhir untuk pemeriksaan berikutnya.
                        conclusionBuffer.delete(0, conclusionBuffer.length - 11)
                    }
                }
            } else {
                // Di dalam kesimpulan; periksa tag penutup.
                conclusionBuffer.append(char)

                // Periksa </conclusion>.
                if (conclusionBuffer.length >= 13) {
                    if (conclusionBuffer.endsWith("</conclusion>")) {
                        // Menemukan tag penutup.
                        inConclusion = false
                        val conclusionContent = conclusionBuffer.toString()
                            .dropLast(13)  // Hapus </conclusion>.
                            .trim()

                        if (conclusionContent.isNotEmpty()) {
                            result.append(conclusionContent).append("\n")
                        }

                        conclusionBuffer.clear()
                        continue
                    } else if (conclusionBuffer.length > 13) {
                        // Simpan 12 karakter terakhir untuk pemeriksaan berikutnya.
                        conclusionBuffer.delete(0, conclusionBuffer.length - 12)
                    }
                }

                // Jika bukan bagian dari tag penutup potensial, tampilkan segera.
                if (!couldBeEndTag(conclusionBuffer.toString())) {
                    result.append(char)
                }
            }
        }

        return if (result.isNotEmpty()) result.toString() else null
    }

    /**
     * Memeriksa apakah akhir buffer bisa menjadi awalan dari tag penutup.
     */
    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) }
    }

    /**
     * Mengatur ulang status parser.
     */
    fun reset() {
        conclusionBuffer.clear()
        inConclusion = false
    }
}

// Contoh penggunaan
MobileAgentSdk().use { sdk ->
    val parser = ConclusionParser()

    lifecycleScope.launch {
        sdk.events
            .filterIsInstance<MobileAgentEvent.Stream>()
            .collect { event ->
                // Uraikan dan cetak output.
                val output = parser.processStream(event.content)
                if (output != null) {
                    print(output)
                }
            }
    }

    sdk.connect("wss://localhost:30005/ws")
}

Versi yang disederhanakan

Jika output real-time tidak diperlukan, Anda dapat mengurai konten setelah seluruhnya diterima:

/**
 * Mengekstrak semua konten dari tag <conclusion> dalam string lengkap.
 */
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
}

// Contoh penggunaan
lifecycleScope.launch {
    sdk.events
        .filterIsInstance<MobileAgentEvent.TaskResult>()
        .collect { event ->
            // Ekstrak proses berpikir dari hasil tugas.
            val conclusions = extractConclusions(event.resultText)

            println("Proses berpikir agen:")
            conclusions.forEachIndexed { index, conclusion ->
                println("${index + 1}. $conclusion")
            }
        }
}

Output

[Thought] The user wants to open the Settings app. I need to find its package name and Activity.
[Thought] By querying the system app list, I found the Settings app: com.android.settings/.Settings
[Thought] Now I will start the Settings app.

Task complete: Successfully opened the settings app.

Menggunakan Flow dan callback bersamaan

Anda dapat menggunakan kedua pendekatan secara bersamaan tanpa gangguan:

MobileAgentSdk().use { sdk ->
    // Gunakan Flow untuk menangani logika utama.
    lifecycleScope.launch {
        sdk.events
            .filterIsInstance<MobileAgentEvent.TaskResult>()
            .collect { event ->
                // Tangani hasil tugas.
            }
    }

    // Gunakan callback untuk logging.
    sdk.setCallback(object : MobileAgentCallback {
        override fun onRawMessage(rawJson: String) {
            // Catat pesan mentah untuk debugging.
            Log.d("SDK_RAW", rawJson)
        }
    })

    sdk.connect("wss://localhost:30005/ws")
}

Penanganan error

try {
    MobileAgentSdk().use { sdk ->
        sdk.connect("wss://localhost:30005/ws")
    }
} catch (e: IllegalStateException) {
    Log.e("SDK", "Gagal membuat SDK", e)
} catch (e: Exception) {
    Log.e("SDK", "Error koneksi", e)
}

Periksa status SDK

val sdk = MobileAgentSdk()

// Periksa apakah SDK telah dihapus.
if (sdk.isDestroyed()) {
    Log.w("SDK", "SDK telah dihapus.")
}

// Periksa apakah SDK valid.
sdk.checkNotDestroyed()  // Melempar exception jika telah dihapus.

Konfigurasi ProGuard

Jika Anda mengaktifkan obfuscation kode, Anda harus mempertahankan metode native SDK. SDK sudah menyertakan file proguard-rules.pro, sehingga biasanya Anda tidak perlu konfigurasi tambahan.

Jika Anda perlu mengonfigurasinya secara manual:

# MobileAgentSDK
-keep class com.wuying.mobileagentsdk.** { *; }
-keepclassmembers class com.wuying.mobileagentsdk.MobileAgentSdk {
    private native <methods>;
}

Dependensi

SDK memiliki dependensi runtime minimal:

dependencies {
    // Satu-satunya dependensi runtime
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}

Untuk tetap ringan, SDK tidak menyertakan framework UI apa pun, seperti AppCompat atau Material.