Untuk membangun aplikasi Wear OS untuk aplikasi Capacitor Anda, tambahkan modul Wear OS native ke dalam android/ proyek, berikan application ID dan signing key yang sama dengan aplikasi ponsel, dan hubungkan keduanya dengan @capgo/capacitor-watch, yang menggunakan Google Wear OS Data Layer di Android. Ponsel tetap menggunakan code TypeScript-nya, dan jam tangan menjalankan aplikasi Kotlin kecil yang bertukar JSON dengannya.
Panduan ini membahas setup modul, jalur Data Layer persis yang digunakan plugin, code Kotlin di jam tangan, code TypeScript di ponsel, pengujian dengan emulator, dan publikasi di Google Play.
Cara kerja jembatan Capacitor ke Wear OS
Wear OS tidak memiliki runtime Capacitor, jadi aplikasi jam tangannya bersifat native. Plugin berkomunikasi melalui API Data Layer (play-services-wearable) di ponsel, dan aplikasi jam tangan Anda menggunakan protokol yang sama.
| Metode plugin / event | Mekanisme Data Layer | Path |
|---|---|---|
sendMessage (dari ponsel ke jam tangan) |
MessageClient |
/capgo/message |
messageReceived (dari jam tangan ke ponsel) |
MessageClient |
/capgo/message |
messageReceivedWithReply |
MessageClient |
/capgo/message/withreply |
replyToMessage |
MessageClient |
/capgo/reply/{callbackId} |
updateApplicationContext / applicationContextReceived |
DataClient DataItem |
/capgo/context |
transferUserInfo / userInfoReceived |
DataClient DataItem |
/capgo/userinfo/{uuid} |
Messages membawa JSON UTF-8 sebagai byte mentah. DataItems membawa JSON yang sama sebagai string di bawah kunci DataMap payload. Ponsel memeriksa keberadaan aplikasi jam tangan dengan mencari capability capgo_watch.
Mengetahui path-path ini adalah kuncinya. Begitu jam tangan membaca dan menulisnya, semua metode plugin akan berfungsi.
Yang Anda butuhkan
- Aplikasi Capacitor 8 dengan platform Android ditambahkan (panduan upgrade jika Anda tertinggal).
- Android Studio terbaru dengan image sistem Wear OS terpasang.
- Ponsel dengan layanan Google Play. Data Layer bergantung padanya, jadi ponsel tanpa layanan Play melaporkan
isSupported: false. - Jam Wear OS atau emulator yang dipasangkan dengan ponsel Android. Jam Wear OS yang dipasangkan dengan iPhone tidak dapat mengakses aplikasi Anda.
Langkah 1: Instal plugin
bun add @capgo/capacitor-watch
bunx cap sync android
Paket yang sama menangani Apple Watch di iOS, jadi aplikasi lintas platform hanya butuh satu import. Lihat dokumentasi plugin untuk referensi lengkap, dan panduan Apple Watch kami kami untuk sisi iOS.
Langkah 2: Tambahkan modul Wear OS ke proyek Android
Buka android/ di Android Studio (bunx cap open android) dan gunakan File > New > New Module > Wear OS. Membiarkan Android Studio menghasilkan modul memberi Anda file Gradle yang sesuai dengan versi Android Gradle Plugin dan Kotlin Anda, yang lebih aman daripada menempelkan file build dari postingan blog. Beri nama modul wear.
Wizard menambahkan include ':wear' ke android/settings.gradle. Biarkan, dan jangan ubah baris yang dikelola Capacitor.
Dua aturan yang diterapkan Data Layer
- Sama
applicationId. Diwear/build.gradle, setapplicationIdke nilai yang persis ada diandroid/app/build.gradle.namespacebisa berbeda, tetapi application ID tidak boleh. - Sertifikat penandatanganan yang sama. Debug builds di satu mesin berbagi debug keystore, jadi ini biasanya berhasil secara lokal lalu gagal di release. Tanda tangani wear release build dengan upload key yang sama seperti aplikasi ponsel.
Jika salah satu aturan dilanggar, kedua aplikasi tidak akan saling melihat dan tidak ada di code yang bisa memperbaikinya.
Dependensi dan minimum SDK
Wear OS 3 ke atas berbasis API 30, jadi minSdk 30 merupakan batas minimum yang praktis untuk aplikasi jam tangan baru. Tambahkan Data Layer dan coroutine helper:
dependencies {
implementation "com.google.android.gms:play-services-wearable:20.0.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.11.0"
// Compose for Wear OS dependencies generated by the wizard stay as they are
}
Deklarasikan kapabilitas
Buat wear/src/main/res/values/wear.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities" translatable="false">
<item>capgo_watch</item>
</string-array>
</resources>
String tersebut harus capgo_watch, karena itulah yang di-query plugin untuk menghitung isWatchAppInstalled.
Tandai aplikasi sebagai aplikasi jam tangan
Di wear/src/main/AndroidManifest.xml, wizard akan menambahkan <uses-feature android:name="android.hardware.type.watch" />. Tentukan apakah aplikasi bisa berjalan tanpa ponsel. Companion yang hanya menampilkan data ponsel harus mendeklarasikan:
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="false" />
Langkah 3: Terima data di jam tangan
Sebuah WearableListenerService memungkinkan sistem mengirim event Data Layer meskipun UI jam tertutup. Daftarkan dengan filter untuk /capgo paths:
<service
android:name=".PhoneListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/capgo" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/capgo" />
</intent-filter>
</service>
Store bersama kecil yang dapat diamati oleh UI:
package com.example.shop.wear
import kotlinx.coroutines.flow.MutableStateFlow
import org.json.JSONObject
object WatchStore {
val context = MutableStateFlow(JSONObject())
val lastMessage = MutableStateFlow(JSONObject())
val lastReply = MutableStateFlow(JSONObject())
}
Layanan ini mem-parse setiap path:
package com.example.shop.wear
import com.google.android.gms.wearable.DataEvent
import com.google.android.gms.wearable.DataEventBuffer
import com.google.android.gms.wearable.DataMapItem
import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService
import org.json.JSONObject
class PhoneListenerService : WearableListenerService() {
override fun onMessageReceived(event: MessageEvent) {
val json = JSONObject(String(event.data, Charsets.UTF_8))
when {
event.path == "/capgo/message" -> WatchStore.lastMessage.value = json
event.path.startsWith("/capgo/reply/") -> WatchStore.lastReply.value = json
}
}
override fun onDataChanged(events: DataEventBuffer) {
for (event in events) {
if (event.type != DataEvent.TYPE_CHANGED) continue
val path = event.dataItem.uri.path ?: continue
val payload = DataMapItem.fromDataItem(event.dataItem)
.dataMap.getString("payload") ?: "{}"
val json = JSONObject(payload)
when {
path == "/capgo/context" -> WatchStore.context.value = json
path.startsWith("/capgo/userinfo/") -> handleUserInfo(json)
}
}
}
private fun handleUserInfo(json: JSONObject) {
// Persist or act on queued events from the phone
}
}
Saat UI jam tangan dibuka, baca juga konteks saat ini secara langsung agar data yang tiba saat aplikasi tertutup ikut tampil:
suspend fun loadCurrentContext(context: android.content.Context) {
val items = Wearable.getDataClient(context).dataItems.await()
try {
for (item in items) {
if (item.uri.path == "/capgo/context") {
val payload = DataMapItem.fromDataItem(item).dataMap.getString("payload") ?: "{}"
WatchStore.context.value = JSONObject(payload)
}
}
} finally {
items.release()
}
}
Langkah 4: Kirim data dari jam tangan
Jam tangan mengirim ke setiap node ponsel yang terhubung. Pesan untuk interaksi langsung, DataItems untuk state.
package com.example.shop.wear
import android.content.Context
import com.google.android.gms.wearable.PutDataMapRequest
import com.google.android.gms.wearable.Wearable
import kotlinx.coroutines.tasks.await
import org.json.JSONObject
import java.util.UUID
object PhoneLink {
suspend fun send(context: Context, data: JSONObject, expectReply: Boolean = false) {
val path = if (expectReply) "/capgo/message/withreply" else "/capgo/message"
val bytes = data.toString().toByteArray(Charsets.UTF_8)
val nodes = Wearable.getNodeClient(context).connectedNodes.await()
for (node in nodes) {
Wearable.getMessageClient(context).sendMessage(node.id, path, bytes).await()
}
}
suspend fun updateContext(context: Context, data: JSONObject) {
// Include a timestamp: identical DataItems are not redelivered
data.put("updatedAt", System.currentTimeMillis())
val request = PutDataMapRequest.create("/capgo/context").apply {
dataMap.putString("payload", data.toString())
setUrgent()
}.asPutDataRequest()
Wearable.getDataClient(context).putDataItem(request).await()
}
suspend fun queueEvent(context: Context, data: JSONObject) {
val request = PutDataMapRequest.create("/capgo/userinfo/${UUID.randomUUID()}").apply {
dataMap.putString("payload", data.toString())
setUrgent()
}.asPutDataRequest()
Wearable.getDataClient(context).putDataItem(request).await()
}
}
Saat ponsel menerima /capgo/userinfo/... item, ia meng-emit userInfoReceived dan menghapus item tersebut, sehingga antrean tidak terus membesar.
Layar Compose minimal menyatukan semuanya:
@Composable
fun OrdersScreen() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val state by WatchStore.context.collectAsState()
val reply by WatchStore.lastReply.collectAsState()
LaunchedEffect(Unit) { loadCurrentContext(context) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("${state.optInt("openOrders", 0)} open orders")
Button(onClick = {
scope.launch {
PhoneLink.send(context, JSONObject().put("action", "refresh"), expectReply = true)
}
}) { Text("Refresh") }
if (reply.has("count")) Text("Phone: ${reply.getInt("count")}")
}
}
Ponsel menghasilkan callbackId, sehingga jam tangan tidak dapat mencocokkan balasan dengan permintaan tertentu. Jika Anda mengirim beberapa permintaan sekaligus, sertakan ID permintaan Anda sendiri di payload dan sertakan kembali di balasan.
Langkah 5: Berkomunikasi dengan jam tangan dari Capacitor code Anda
Sisi TypeScript-nya sama dengan code yang akan Anda tulis untuk Apple Watch:
import { Capacitor } from '@capacitor/core';
import { CapgoWatch } from '@capgo/capacitor-watch';
export async function initWearBridge(loadOpenOrders: () => Promise<number>) {
if (Capacitor.getPlatform() === 'web') return;
await CapgoWatch.addListener('messageReceivedWithReply', async ({ message, callbackId }) => {
if (message.action === 'refresh') {
const count = await loadOpenOrders();
await CapgoWatch.replyToMessage({ callbackId, data: { count } });
}
});
await CapgoWatch.addListener('applicationContextReceived', ({ context }) => {
console.log('watch state', context);
});
await CapgoWatch.addListener('userInfoReceived', ({ userInfo }) => {
console.log('queued event from watch', userInfo);
});
}
export async function syncToWatch(openOrders: number) {
const info = await CapgoWatch.getInfo();
if (!info.isSupported || !info.isWatchAppInstalled) return;
await CapgoWatch.updateApplicationContext({ context: { openOrders } });
if (info.isReachable) {
await CapgoWatch.sendMessage({ data: { toast: 'Orders updated' } });
}
}
Hal spesifik Android yang perlu diketahui:
sendMessageditolak dengan pesan “No connected Wear OS devices found” jika tidak ada node watch yang terhubung.isPaireddanisReachablekeduanya berarti “minimal satu node Wear OS terhubung”.isWatchAppInstalledberarti sebuah node mengiklankancapgo_watch.- Reachability tidak didorong sebagai event di Android saat ini. Panggil
getInfo()ketika Anda membutuhkan nilai terbaru, misalnya saat layar mendapatkan fokus. - Balasan yang tertunda kedaluwarsa setelah lima menit di sisi ponsel.
Channel mana yang harus digunakan
| Butuh | Gunakan | Mengapa |
|---|---|---|
| Tekan tombol Live, buka aplikasi watch | sendMessage / /capgo/message |
Cepat, tapi hilang jika tidak ada node yang terhubung |
| Apa yang harus ditampilkan jam | updateApplicationContext / /capgo/context |
Nilai terbaru disimpan dan disinkronkan nanti |
| Event yang tidak boleh hilang | transferUserInfo / /capgo/userinfo/{uuid} |
Setiap event adalah DataItem tersendiri |
Batasan Data Layer
- DataItems dibatasi sekitar 100 KB. Pesan juga sebaiknya tetap kecil. Kirim ID dan ambil konten dari backend Anda.
- Menulis DataItem dengan konten identik tidak memicu change event. Tambahkan timestamp jika Anda membutuhkan event baru.
- Pesan ke jam tangan memulai
WearableListenerService, bukan UI Anda. - Di ponsel, listener plugin ada selama proses aplikasi Capacitor berjalan. Pesan yang dikirim saat proses mati tidak diputar ulang ke JavaScript. DataItems tetap ada di Data Layer, jadi gunakan context untuk data apa pun yang perlu dilihat ponsel nanti.
- Payload berupa objek JSON. Array tingkat atas dan nilai non-JSON tidak akan diparsing.
Testing dengan emulator dan perangkat
- Buat emulator ponsel dengan Google Play system image dan emulator Wear OS di Device Manager.
- Gunakan Wear OS pairing assistant di Android Studio untuk memasangkannya. Alat ini menginstal companion app di emulator ponsel.
- Jalankan
wearkonfigurasi pada emulator jam tangan, lalu jalankan aplikasi Capacitor Anda di ponsel denganbunx cap run android. - Panggil
CapgoWatch.getInfo()dari ponsel.isWatchAppInstalled: truemengonfirmasi kecocokan capability dan package.
Untuk jam tangan fisik, aktifkan developer options dan ADB debugging lewat Wi-Fi di jam, lalu hubungkan dengan adb connect <watch-ip>:<port>.
Pemecahan Masalah
isSupported: false. Google Play services atau Wear OS API tidak ada di ponsel. Umum terjadi pada emulator tanpa Play images.
isWatchAppInstalled: false saat jam sedang terhubung. Panduan capgo_watch capability tidak ada atau salah ketik, atau aplikasi wear belum terinstal di jam tersebut.
Pesan tidak pernah sampai ke jam tangan. Berbeda applicationId atau signing keys yang berbeda. Bandingkan ./gradlew :app:signingReport dan ./gradlew :wear:signingReport.
Update Context berhenti masuk. Anda mengirim payload yang sama berulang kali. Tambahkan field yang berubah.
Release build berjalan di debug tapi tidak di Play. Jika Anda menggunakan Play App Signing, artifact ponsel dan jam tangan harus ditandatangani dengan kunci penandatanganan aplikasi yang sama di aplikasi Play Console yang sama.
R8 menghapus service Anda. Pertahankan class yang hanya dirujuk dari manifest jika Anda menggunakan aturan shrinking kustom.
Publikasi di Google Play
Aplikasi Wear OS dirilis di bawah aplikasi Play Console yang sama dengan aplikasi ponsel, karena mereka berbagi nama paket. Tambahkan form factor Wear OS di Play Console, upload bundle wear ke track rilis Wear OS, dan tambahkan screenshot jam tangan. Google meninjau aplikasi Wear OS berdasarkan pedoman kualitas Wear OS, jadi periksa tata letak layar bulat dan ukuran target sentuh.
Untuk pembaruan, Live updates Capgo dapat mendorong perbaikan ke lapisan web aplikasi ponsel tanpa rilis toko, yang mencakup bug di bridge TypeScript Anda code. Perubahan Kotlin di jam tangan melalui Google Play. Versikan payload JSON Anda agar bundle ponsel yang lebih baru tetap kompatibel dengan build jam tangan yang lebih lama. Jika Anda membangun aplikasi ponsel di cloud, Build Capgo menangani build Android Capacitor dan penandatanganan, dan Anda dapat menghasilkan keystore yang cocok dengan Generator keystore Android.
Kesimpulan
Pendamping Wear OS untuk aplikasi Capacitor terdiri dari tiga bagian: @capgo/capacitor-watch di ponsel, modul Kotlin yang membagikan application ID dan signing key ponsel, dan /capgo/* jalur Data Layer di antaranya. Dapatkan kemampuan dan penandatanganan yang benar terlebih dahulu, lalu code akan singkat.