To build a Wear OS app for your Aplikasi Capacitor app, add a native Wear OS module to the android/ project, berikan ID aplikasi dan kunci tanda tangan yang sama seperti aplikasi ponsel, dan hubungkan kedua aplikasi tersebut dengan @capgo/capacitor-watchyang menggunakan Layer Data Wear OS dari Google di Android. Telepon menjaga TypeScript code, dan jam tangan menjalankan aplikasi Kotlin kecil yang berkomunikasi dengan JSON-nya.
Petunjuk ini menjelaskan cara mengatur modul, jalur Layer Data yang plugin gunakan secara tepat, aplikasi Kotlin code pada jam tangan, aplikasi TypeScript code pada telepon, melakukan tes dengan emulator, dan menerbitkan di Google Play.
Cara Capacitor ke bridge Wear OS berfungsi
Wear OS tidak memiliki Capacitor runtime, jadi aplikasi jam tangan adalah native. Plugin berbicara protokol Layer Data API (play-services-wearable) pada telepon, dan aplikasi jam tangan berbicara protokol yang sama.
| Metode / event plugin | Mechanisme Layer Data | Jalur |
|---|---|---|
sendMessage (telepon ke jam tangan) |
MessageClient |
/capgo/message |
messageReceived (jam tangan ke telepon) |
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} |
Message membawa JSON UTF-8 sebagai byte mentah. DataItem membawa JSON yang sama sebagai string di bawah kunci DataMap payloadTelepon mengecek aplikasi jam dengan mencari kemampuan yang tersedia capgo_watch.
Mengetahui jalur ini adalah trik utama. Setelah jam tangan membaca dan menulisnya, setiap metode plugin berfungsi
Apa yang Anda butuhkan
- Aplikasi Capacitor 8 dengan platform Android ditambahkan (petunjuk pembaruan jika Anda tertinggal).
- Aplikasi Android Studio yang baru dengan gambar sistem Wear OS diinstal.
- Telepon dengan layanan Google Play. Layer Data bergantung pada itu, jadi telepon tanpa layanan Play melaporkan
isSupported: false. - Aplikasi jam tangan Wear OS atau emulator yang dipasangkan dengan telepon Android. Jam tangan Wear OS yang dipasangkan dengan iPhone tidak dapat mencapai aplikasi Anda.
Langkah 1: Pasang plugin
bun add @capgo/capacitor-watch
bunx cap sync android
Kemasan yang sama mengelola Apple Watch di iOS, jadi aplikasi lintas platform memerlukan satu import. Lihat Dokumentasi Plugin untuk referensi lengkap, dan kami Trik Membuat Aplikasi Pakaian Apple Watch untuk sisi iOS.
Langkah 2: Tambahkan modul Wear OS ke proyek Android
Buka android/ dalam Studio Android ("bunx cap open androiddan gunakan Buka File > Baru > Modul Baru > Wear OSMenggunakan Android Studio untuk menghasilkan modul akan memberikan Anda file Gradle yang sesuai dengan versi Android Gradle Plugin dan Kotlin Anda, yang lebih aman daripada menyalin file build dari posting blog. Beri nama modul wear.
Pengguna Wizard include ':wear' Ke android/settings.gradleJaga, dan biarkan baris-baris yang diatur oleh Capacitor tetap sama.
Ada dua aturan yang ditegakkan oleh Layer Data.
- Sama.
applicationId. Dalamwear/build.gradle, tetapkanapplicationIdke nilai yang tepat diandroid/app/build.gradle. Nilainamespacedapat berbeda, tetapi ID aplikasi tidak boleh. - Sertifikat tanda tangan yang sama. Jika Anda membuat aplikasi debug di satu mesin, maka keystore debug akan digunakan, sehingga biasanya berfungsi secara lokal dan kemudian gagal dalam versi rilis. Tandatangani aplikasi rilis Wear dengan kunci unggah yang sama seperti aplikasi ponsel.
Jika salah satu aturan ini dilanggar, dua aplikasi tidak pernah melihat satu sama lain dan tidak ada yang dapat memperbaikinya di dalam code.
Ketergantungan dan versi minimum SDK
Wear OS 3 dan seterusnya berdasarkan API 30, jadi minSdk 30 adalah lantai yang praktis untuk aplikasi jam baru. Tambahkan Layer Data dan bantuan coroutine:
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
}
Kemukakan kemampuan
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>
The string must be capgo_watch, karena itu adalah apa plugin yang ditanyakan untuk menghitung isWatchAppInstalled.
Mark aplikasi sebagai aplikasi jam
Dalam wear/src/main/AndroidManifest.xml, sihir ajaib menambahkan <uses-feature android:name="android.hardware.type.watch" />. Putuskan apakah aplikasi berfungsi tanpa ponsel. Sebuah teman yang hanya menampilkan data ponsel harus mengumumkan:
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="false" />
Langkah 3: Terima data di jam
context WearableListenerService Biarkan sistem mengirimkan Data Layer events bahkan ketika antarmuka UI jam tangan ditutup. Daftarkan dengan filter untuk /capgo jalur:
<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>
Penyimpanan bersama kecil yang UI dapat mengamati:
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())
}
Pelayanan menganalisis setiap jalur:
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
}
}
Ketika antarmuka jam tangan dibuka, baca konteks saat ini secara langsung sehingga menampilkan data yang telah datang saat aplikasi ditutup.
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
Pengiriman jam tangan ke setiap node telepon terhubung. Pesan-pesan ini untuk interaksi langsung, DataItems untuk keadaan.
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()
}
}
Ketika ponsel menerima sinyal /capgo/userinfo/... item yang dihasilkannya userInfoReceived dan menghapus item tersebut, sehingga antrian tidak akan terus tumbuh.
Sebuah layar Compose minimal yang menghubungkannya:
@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")}")
}
}
Perangkat telepon menghasilkan callbackIdJadi, jam tangan tidak dapat menemukan jawaban untuk permintaan tertentu. Jika Anda mengirimkan beberapa permintaan sekaligus, masukkan ID permintaan Anda sendiri ke dalam payload dan ulangi kembali dalam jawaban.
Langkah 5: Berbicara dengan jam tangan dari Capacitor code
The TypeScript side is the same code you would write for 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' } });
}
}
Spesifikasi Android yang perlu diketahui:
sendMessageTidak menemukan perangkat Wear OS apa pun ketika tidak ada node jam terhubung.isPaireddanisReachablekeduanya berarti “setidaknya satu node Wear OS terhubung”.isWatchAppInstalledberarti sebuah node mengiklankancapgo_watch.- Ketersediaan tidak dipush sebagai event pada Android hari ini. Panggil
getInfo()ketika Anda membutuhkan nilai yang segar, misalnya ketika layar mendapatkan fokus. - Pertanyaan yang belum dijawab akan berakhir setelah lima menit pada sisi ponsel.
Mana saluran yang harus digunakan
| Perlu | Menggunakan | Mengapa |
|---|---|---|
| Tombol Live tekan, aplikasi jam tangan terbuka | sendMessage / /capgo/message |
Cepat, tetapi jatuh jika tidak ada node yang terhubung |
| Apa yang harus ditampilkan pada jam tangan | updateApplicationContext / /capgo/context |
Nilai terbaru disimpan dan disinkronkan kemudian |
| Event yang tidak boleh hilang | transferUserInfo / /capgo/userinfo/{uuid} |
Setiap event adalah DataItem sendiri |
Keterbatasan Layer Data
- DataItem terbatas sekitar 100 KB. Pesan harus tetap kecil juga. Kirim ID dan ambil konten dari backend Anda.
- Menggunakan DataItem dengan konten identik tidak akan memicu perubahan event. Tambahkan timestamp jika Anda membutuhkan event yang segar.
- A pesan ke jam tangan memulai sesuatu
WearableListenerService, bukan UI Anda. - Pada ponsel, pengguna plugin ada saat proses aplikasi Capacitor berjalan. Pesan yang dikirim saat aplikasi mati tidak akan diputar kembali ke JavaScript. DataItem tetap ada di Layer Data, jadi gunakan konteks untuk apa pun yang ponsel harus lihat kemudian.
- Payloads adalah objek JSON. Array tingkat atas dan nilai non-JSON tidak akan diproses.
Menguji dengan emulator dan perangkat
- Buat emulator ponsel dengan gambar sistem Google Play dan emulator Wear OS di Device Manager.
- Gunakan asisten pairing Wear OS Android Studio untuk memasangkannya. Ini akan menginstal aplikasi kompanen pada emulator ponsel.
- Jalankan
wearkonfigurasi pada emulator jam tangan, kemudian jalankan aplikasi Capacitor Anda pada ponsel denganbunx cap run android. - Panggilan
CapgoWatch.getInfo()mengkonfirmasi kemampuan dan paket sesuai.isWatchAppInstalled: truemengkonfirmasi kemampuan dan paket sesuai.
Untuk jam tangan fisik, aktifkan opsi pengembang dan debugging ADB melalui Wi-Fi pada jam, kemudian hubungkan dengan adb connect <watch-ip>:<port>.
Troubleshooting
isSupported: false. Jasa Google Play atau Wear OS API tidak terinstal di ponsel. Biasanya terjadi pada emulator tanpa gambar Play.
isWatchAppInstalled: false Saat jam tangan terhubung. The capgo_watch Kemampuan tersebut tidak ada atau salah eja, atau aplikasi jam tidak terinstal di jam tersebut.
Pesan tidak pernah sampai di jam. Different applicationId atau kunci tanda tangan yang berbeda. Bandingkan ./gradlew :app:signingReport and ./gradlew :wear:signingReport.
Pembaruan konteks berhenti sampai. Anda menulis payload yang sama berulang-ulang. Tambahkan bidang yang berubah.
Rilis build berfungsi pada debug tetapi tidak pada Play. Jika Anda menggunakan Play App Signing, baik artefak ponsel dan jam tangan harus ditandatangani oleh kunci tanda tangan aplikasi yang sama di aplikasi Play Console yang sama.
R8 menghilangkan layanan Anda. Tetapkan kelas yang hanya dapat diakses dari manifest jika Anda menggunakan aturan pemangkasan kustom.
Penerbitan di Google Play
Aplikasi Wear OS dikirimkan di bawah aplikasi Play Console yang sama dengan aplikasi ponsel, karena mereka memiliki nama paket yang sama. Tambahkan faktor bentuk Wear OS di Play Console, unggah bundle Wear OS ke jalur rilis Wear OS, dan tambahkan layar tangkapan jam tangan. Google meninjau aplikasi Wear OS terhadap pedoman kualitas Wear OS, jadi periksa tata letak layar lingkaran dan ukuran target sentuh.
Untuk pembaruan, Capgo pembaruan hidup can push fixes to the phone app’s web layer without a store release, which covers bugs in your TypeScript bridge code. Kotlin changes on the watch go through Google Play. Version your JSON payloads so a newer phone bundle stays compatible with an older watch build. If you build the phone app in the cloud, Capgo Bangun handles the Capacitor Android build and signing, and you can generate a matching keystore with the Generator Kunci Keamanan Android.
Penutupan
Apa itu aplikasi Wear OS untuk sebuah aplikasi Capacitor? Ini terdiri dari tiga bagian: @capgo/capacitor-watch di ponsel, sebuah modul Kotlin yang berbagi ID aplikasi dan kunci tanda tangan ponsel, serta /capgo/* Lapisan Data jalur di antara. Dapatkan kemampuan dan tanda tangan yang tepat terlebih dahulu, kemudian code yang singkat.