رویدادهای لایه داده را در Wear مدیریت کنید

وقتی شما یک فراخوانی به API لایه داده (Data Layer API) انجام می‌دهید، می‌توانید وضعیت آن را پس از اتمام فراخوانی دریافت کنید. همچنین می‌توانید به رویدادهای داده‌ای ناشی از تغییرات داده‌ای که برنامه شما در هر کجای شبکه Wear OS by Google ایجاد می‌کند، گوش دهید.

برای مثالی از کار مؤثر با API لایه داده، به برنامه نمونه لایه داده اندروید مراجعه کنید.

منتظر وضعیت فراخوانی‌های لایه داده باشید

فراخوانی‌های API لایه داده - مانند فراخوانی با استفاده از متد putDataItem از کلاس DataClient - گاهی اوقات یک شیء Task<ResultType> را برمی‌گردانند. به محض ایجاد شیء Task ، عملیات در پس‌زمینه در صف قرار می‌گیرد. اگر بعد از این کار دیگری انجام ندهید، عملیات در نهایت به طور بی‌صدا تکمیل می‌شود.

با این حال، شما معمولاً می‌خواهید پس از اتمام عملیات، کاری با نتیجه انجام دهید، بنابراین شیء Task به شما امکان می‌دهد تا منتظر وضعیت نتیجه باشید، چه به صورت ناهمگام و چه به صورت همگام.

فراخوانی‌های ناهمزمان

اگر کد شما روی نخ اصلی رابط کاربری (UI thread) اجرا می‌شود، فراخوانی‌های مسدودکننده به API لایه داده (Data Layer API) انجام ندهید و از یک کوروتین (Coroutine) برای فراخوانی putDataItem استفاده کنید:

private suspend fun Context.sendDataAsync(count: Int) {
    try {
        val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run {
            dataMap.putInt("count_key", count)
            asPutDataRequest()
        }
        val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await()
        handleDataItem(dataItem)
    } catch (e: Exception) {
        handleDataItemError(e)
    } finally {
        handleTaskComplete()
    }
}

private fun handleDataItem(dataItem: DataItem) { }
private fun handleDataItemError(exception: Exception) { }
private fun handleTaskComplete() { }

برای سایر امکانات، از جمله زنجیره‌سازی اجرای وظایف مختلف، به API وظایف مراجعه کنید.

تماس‌های همزمان

اگر کد شما روی یک نخ کنترل‌کننده‌ی جداگانه در یک سرویس پس‌زمینه، مانند WearableListenerService ، اجرا می‌شود، runBlocking برای ایجاد یک فراخوانی مسدودکننده به putDataItem استفاده کنید.

توجه: این را در حالی که در تاپیک اصلی هستید فراخوانی نکنید.

private fun Context.sendDataSync(count: Int) = runBlocking {
    val putDataReq = PutDataMapRequest.create("/count").run {
        dataMap.putInt("count_key", count)
        asPutDataRequest()
    }

    try {
        val result = Wearable.getDataClient(this@sendDataSync)
            .putDataItem(putDataReq)
            .await()
        // Logic for success
    } catch (e: Exception) {
        // Handle failure
    }
}

به رویدادهای لایه داده گوش دهید

از آنجا که لایه داده، داده‌ها را بین دستگاه‌های دستی و پوشیدنی همگام‌سازی و ارسال می‌کند، معمولاً باید به رویدادهای مهمی مانند ایجاد اقلام داده و دریافت پیام‌ها گوش دهید.

برای گوش دادن به رویدادهای لایه داده، دو گزینه دارید:

با هر دوی این گزینه‌ها، شما متدهای فراخوانی رویداد داده را برای رویدادهایی که می‌خواهید مدیریت کنید، بازنویسی می‌کنید.

نکته: هنگام انتخاب پیاده‌سازی شنونده، میزان مصرف باتری برنامه خود را در نظر بگیرید. یک WearableListenerService در مانیفست برنامه ثبت شده است و اگر برنامه در حال اجرا نباشد، می‌تواند آن را اجرا کند. اگر فقط زمانی که برنامه شما در حال اجرا است نیاز به گوش دادن به رویدادها دارید، که اغلب در برنامه‌های تعاملی اتفاق می‌افتد، از WearableListenerService استفاده نکنید. در عوض، یک شنونده زنده ثبت کنید. به عنوان مثال، از متد addListener از کلاس DataClient استفاده کنید. این می‌تواند بار سیستم و مصرف باتری را کاهش دهد.

از یک WearableListenerService استفاده کنید

شما معمولاً نمونه‌هایی از WearableListenerService هم در برنامه‌های پوشیدنی و هم در برنامه‌های دستی خود ایجاد می‌کنید. با این حال، اگر به رویدادهای داده در یکی از برنامه‌ها علاقه‌ای ندارید، نیازی به پیاده‌سازی سرویس در آن برنامه ندارید.

برای مثال، می‌توانید یک برنامه‌ی دستی داشته باشید که اشیاء آیتم داده را تنظیم و دریافت می‌کند و یک برنامه‌ی پوشیدنی که برای به‌روزرسانی رابط کاربری خود به این به‌روزرسانی‌ها گوش می‌دهد. برنامه‌ی پوشیدنی هرگز هیچ یک از آیتم‌های داده را به‌روزرسانی نمی‌کند، بنابراین برنامه‌ی دستی به هیچ رویداد داده‌ای از برنامه‌ی پوشیدنی گوش نمی‌دهد.

برخی از رویدادهایی که می‌توانید با استفاده از WearableListenerService به آنها گوش دهید، عبارتند از:

  • onDataChanged() : هر زمان که یک شیء داده ایجاد، حذف یا تغییر کند، سیستم این فراخوانی را روی تمام گره‌های متصل آغاز می‌کند.
  • onMessageReceived() : پیامی که از یک گره ارسال می‌شود، این فراخوانی مجدد را در گره هدف فعال می‌کند.
  • onCapabilityChanged() : وقتی قابلیتی که یک نمونه از برنامه شما تبلیغ می‌کند در شبکه در دسترس قرار می‌گیرد، آن رویداد این فراخوانی را فعال می‌کند. اگر به دنبال یک گره نزدیک هستید، می‌توانید از متد isNearby() گره‌های ارائه شده در فراخوانی، کوئری بگیرید.

همچنین می‌توانید به رویدادهای ChannelClient.ChannelCallback مانند onChannelOpened() گوش دهید.

تمام رویدادهای قبلی در یک نخ پس‌زمینه اجرا می‌شوند، نه در نخ اصلی.

برای ایجاد یک WearableListenerService ، مراحل زیر را دنبال کنید:

  1. یک کلاس ایجاد کنید که WearableListenerService ارث‌بری کند.
  2. به دنبال رویدادهایی که به آنها علاقه‌مند هستید، مانند onDataChanged() بگردید.
  3. یک فیلتر intent در مانیفست اندروید خود تعریف کنید تا سیستم را از WearableListenerService خود مطلع کند. این تعریف به سیستم اجازه می‌دهد تا در صورت نیاز، سرویس شما را bind کند.

مثال زیر نحوه پیاده‌سازی WearableListenerService را نشان می‌دهد:

class DataLayerListenerService : WearableListenerService() {

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onDataChanged: $dataEvents")
        }

        // Loop through the events and send a message
        // to the node that created the data item.
        dataEvents
            .map { it.dataItem.uri }
            .forEach { uri ->
                // Get the node ID from the host value of the URI.
                val nodeId: String = uri.host!!
                // Set the data of the message to be the bytes of the URI.
                val payload: ByteArray = uri.toString().toByteArray()

                // Send the RPC.
                Wearable.getMessageClient(this)
                    .sendMessage(
                        nodeId,
                        DATA_ITEM_RECEIVED_PATH,
                        payload
                    )
            }
    }
}

بخش زیر نحوه استفاده از فیلتر intent با این شنونده را توضیح می‌دهد.

استفاده از فیلترها با WearableListenerService

یک فیلتر intent برای مثال WearableListenerService که در بخش قبلی نشان داده شد، ممکن است به شکل زیر باشد:

<service
    android:name=".snippets.datalayer.DataLayerListenerService"
    android:exported="true"
    tools:ignore="ExportedService" >
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
        <data
            android:scheme="wear"
            android:host="*"
            android:path="/start-activity" />
    </intent-filter>
</service>

فیلتر اکشن DATA_CHANGED به سیستم می‌گوید که برنامه شما به رویدادهای لایه داده علاقه‌مند است.

در این مثال، ساعت منتظر آیتم داده‌ای /start-activity ‎ و تلفن منتظر پاسخ پیام /data-item-received ( DATA_ITEM_RECEIVED_PATH )‎ است.

قوانین استاندارد تطبیق فیلتر اندروید اعمال می‌شود. می‌توانید چندین سرویس را در هر مانیفست، چندین فیلتر intent را در هر سرویس، چندین اکشن را در هر فیلتر و چندین بخش داده را در هر فیلتر مشخص کنید. فیلترها می‌توانند روی یک میزبان wildcard یا روی یک میزبان خاص تطبیق داده شوند. برای تطبیق روی یک میزبان wildcard، host="*" استفاده کنید. برای تطبیق روی یک میزبان خاص، host=<node_id> را مشخص کنید.

همچنین می‌توانید یک مسیر تحت‌اللفظی یا پیشوند مسیر را تطبیق دهید. برای انجام این کار، باید یک wildcard یا میزبان خاص را مشخص کنید. در غیر این صورت، سیستم مسیری را که مشخص می‌کنید نادیده می‌گیرد.

برای اطلاعات بیشتر در مورد انواع فیلترهایی که Wear OS پشتیبانی می‌کند، به مستندات مرجع API برای WearableListenerService مراجعه کنید.

برای اطلاعات بیشتر در مورد فیلترهای داده و قوانین تطبیق، به مستندات مرجع API برای عنصر مانیفست <data> مراجعه کنید.

هنگام تطبیق فیلترهای هدف، دو قانون مهم را به خاطر داشته باشید:

  • اگر هیچ طرحی برای فیلتر intent مشخص نشده باشد، سیستم تمام ویژگی‌های دیگر URI را نادیده می‌گیرد.
  • اگر هیچ میزبانی برای فیلتر مشخص نشده باشد، سیستم تمام ویژگی‌های مسیر را نادیده می‌گیرد.

از یک شنونده زنده استفاده کنید

اگر برنامه شما فقط به رویدادهای لایه داده در زمان تعامل کاربر با برنامه اهمیت می‌دهد، ممکن است به یک سرویس طولانی مدت برای مدیریت هر تغییر داده نیاز نداشته باشد. در چنین حالتی، می‌توانید به رویدادها در یک activity گوش دهید.

برای توصیه یک رویکرد تمیزتر و ایمن‌تر، از یک Lifecycle Observer استفاده کنید. با استفاده از Lifecycle Observer، منطق ثبت را از onResume() مربوط به Activity خارج کرده و به یک کلاس جداگانه و قابل استفاده مجدد که DefaultLifecycleObserver پیاده‌سازی می‌کند، منتقل می‌کنید.

این رویکرد، Activity شما را سبک نگه می‌دارد و از باگ‌های رایجی مانند فراموش کردن لغو ثبت شنونده جلوگیری می‌کند.

۱. شنونده‌ای آگاه از چرخه حیات ایجاد کنید

این کلاس DataClient.OnDataChangedListener را در بر می‌گیرد و به طور خودکار اشتراک خود را بر اساس چرخه حیات Activity مدیریت می‌کند.

class WearDataLayerObserver(
    private val dataClient: DataClient,
    private val onDataReceived: (DataEventBuffer) -> Unit
) : DefaultLifecycleObserver, DataClient.OnDataChangedListener {

    // Implementation of the DataClient listener
    override fun onDataChanged(dataEvents: DataEventBuffer) {
        onDataReceived(dataEvents)
    }

    // Automatically register when the Activity starts
    override fun onResume(owner: LifecycleOwner) {
        dataClient.addListener(this)
    }

    // Automatically unregister when the Activity pauses
    override fun onPause(owner: LifecycleOwner) {
        dataClient.removeListener(this)
    }
}

۲. کاربرد در اکتیویتی شما

حالا، Activity شما نیازی به override کردن onResume() یا onPause() برای Wear API ندارد. شما یک بار در onCreate() ناظر را اضافه می‌کنید.

class DataLayerLifecycleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val dataClient = Wearable.getDataClient(this)

        // Create the observer and link it to the activity's lifecycle
        val wearObserver = WearDataLayerObserver(dataClient) { dataEvents ->
            handleDataEvents(dataEvents)
        }

        lifecycle.addObserver(wearObserver)
    }

    private fun handleDataEvents(dataEvents: DataEventBuffer) {
        // ... filter and process events ...
    }
}

چرا این بهتر است:

  • فعالیت پاک‌کننده: شما کدهای تکراری را از متدهای چرخه حیات فعالیت حذف می‌کنید.
  • ایمنی: DefaultLifecycleObserver به تأیید حذف شنونده حتی در صورت از بین رفتن غیرمنتظره‌ی اکتیویتی کمک می‌کند و از نشت حافظه جلوگیری می‌کند.
  • قابلیت استفاده مجدد: شما می‌توانید این WearDataLayerObserver بدون بازنویسی منطق ثبت، به هر Activity یا Fragment متصل کنید.
  • جداسازی: منطق زمان گوش دادن از منطق نحوه‌ی برخورد با داده‌ها جدا شده است.

استفاده از فیلترها با شنوندگان زنده

همانطور که قبلاً ذکر شد، همانطور که می‌توانید فیلترهای intent را برای اشیاء WearableListenerService مبتنی بر مانیفست مشخص کنید، می‌توانید هنگام ثبت یک شنونده زنده از طریق Wearable API از فیلترهای intent استفاده کنید. قوانین یکسانی هم برای شنونده‌های زنده مبتنی بر API و هم برای شنونده‌های مبتنی بر مانیفست اعمال می‌شود.

A common pattern is to register a listener with a specific path or path prefix using a LifecycleObserver . By implementing listeners in this fashion, your app can more selectively receive events, improving its design and efficiency.