יצירת ווידג'ט מתקדם

כדאי לנסות את הכתיבה
‫Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ל-Android. איך יוצרים ווידג'טים באמצעות ממשקי API בסגנון Compose

בדף הזה מוסברות שיטות מומלצות ליצירת ווידג'ט מתקדם יותר כדי לשפר את חוויית המשתמש.

אופטימיזציות לעדכון תוכן הווידג'ט

עדכון תוכן הווידג'ט יכול להיות יקר מבחינת משאבי מחשוב. כדי לחסוך בצריכת הסוללה, כדאי לבצע אופטימיזציה של סוג העדכון, התדירות והתזמון.

סוגי עדכונים לווידג'טים

יש שלוש דרכים לעדכן ווידג'ט: עדכון מלא, עדכון חלקי ורענון נתונים (במקרה של ווידג'ט אוסף). לכל אחד מהם יש עלויות חישוב והשלכות שונות.

בהמשך מפורט כל סוג עדכון, ומוצגים קטעי קוד לכל סוג.

  • עדכון מלא: מתקשרים אל AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) כדי לעדכן את הווידג'ט באופן מלא. הפעולה הזו תחליף את RemoteViews שסופק קודם ב-RemoteViews חדש. זהו העדכון הכי יקר מבחינת משאבי מחשוב.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
    }
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews)

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1");
    remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2");
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
  • עדכון חלקי: קוראים ל-AppWidgetManager.partiallyUpdateAppWidget כדי לעדכן חלקים בווידג'ט. הפעולה הזו תמזג את RemoteViews החדש עם RemoteViews שסופק קודם. המערכת מתעלמת מהשיטה הזו אם הווידג'ט לא מקבל לפחות עדכון מלא אחד דרך updateAppWidget(int[], RemoteViews).

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
    }
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text");
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
  • רענון נתוני האוסף: קריאה ל-AppWidgetManager.notifyAppWidgetViewDataChanged כדי לבטל את תוקף הנתונים של תצוגת אוסף בווידג'ט. הפעולה הזו מפעילה את RemoteViewsFactory.onDataSetChanged. בינתיים, הנתונים הישנים מוצגים בווידג'ט. באמצעות השיטה הזו אפשר לבצע משימות יקרות באופן סינכרוני בצורה בטוחה.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);

אפשר להפעיל את השיטות האלה מכל מקום באפליקציה, כל עוד לאפליקציה יש את אותו UID כמו למחלקה התואמת AppWidgetProvider.

קביעת התדירות של עדכון הווידג'ט

הווידג'טים מתעדכנים מעת לעת בהתאם לערך שצוין במאפיין updatePeriodMillis. הווידג'ט יכול להתעדכן בתגובה לאינטראקציה של המשתמש, לשדר עדכונים או לבצע את שתי הפעולות.

עדכון תקופתי

כדי לשלוט בתדירות העדכון התקופתי, מציינים ערך ל-AppWidgetProviderInfo.updatePeriodMillis ב-XML של appwidget-provider. כל עדכון מפעיל את השיטה AppWidgetProvider.onUpdate(), שבה אפשר להציב את הקוד לעדכון הווידג'ט. עם זאת, אם הווידג'ט צריך לטעון נתונים באופן אסינכרוני או שלוקח לו יותר מ-10 שניות להתעדכן, כדאי לשקול את החלופות לעדכונים של מקלטי שידורים שמתוארות בקטע הבא, כי אחרי 10 שניות המערכת מחשיבה את BroadcastReceiver כלא מגיב.

הפרמטר updatePeriodMillis לא תומך בערכים של פחות מ-30 דקות. עם זאת, אם רוצים להשבית עדכונים תקופתיים, אפשר לציין 0.

אתם יכולים לאפשר למשתמשים לשנות את תדירות העדכונים בהגדרה. לדוגמה, הם יכולים לרצות שטיקר של מניות יתעדכן כל 15 דקות או רק ארבע פעמים ביום. במקרה כזה, צריך להגדיר את updatePeriodMillis כ-0 ולהשתמש ב-WorkManager.

עדכון בתגובה לאינטראקציה של משתמש

ריכזנו כאן כמה דרכים מומלצות לעדכן את הווידג'ט על סמך אינטראקציה של המשתמשים:

  • מפעילות של האפליקציה: קריאה ישירה ל-AppWidgetManager.updateAppWidget בתגובה לאינטראקציה של משתמש, כמו הקשה של משתמש.

  • מאינטראקציות מרחוק, כמו התראה או ווידג'ט של אפליקציה: יוצרים PendingIntent, ואז מעדכנים את הווידג'ט מה-Activity,‏ Broadcast או Service שהופעלו. אתם יכולים לבחור את העדיפות שלכם. לדוגמה, אם בוחרים Broadcast עבור PendingIntent, אפשר לבחור שידור בחזית כדי לתת עדיפות לBroadcastReceiver.

עדכון בתגובה לאירוע שידור

דוגמה לאירוע שידור שדורש עדכון של הווידג'ט היא כשמשתמש מצלם תמונה. במקרה הזה, רוצים לעדכן את הווידג'ט כשמזוהה תמונה חדשה.

אפשר לתזמן עבודה עם JobScheduler ולציין שידור כטריגר באמצעות השיטה JobInfo.Builder.addTriggerContentUri.

אפשר גם להירשם לשידור – לדוגמה, להירשם להאזנה לACTION_LOCALE_CHANGED.BroadcastReceiver עם זאת, מכיוון שהפעולה הזו צורכת משאבי מכשיר, צריך להשתמש בה בזהירות ולהאזין רק לשידור הספציפי. עם ההשקה של מגבלות על שידורים ב-Android 7.0 (רמת API‏ 24) וב-Android 8.0 (רמת API‏ 26), אפליקציות לא יכולות לרשום שידורים מרומזים במניפסטים שלהן, עם חריגים מסוימים.

שיקולים כשמעדכנים ווידג'ט מ-BroadcastReceiver

אם הווידג'ט מתעדכן מ-BroadcastReceiver, כולל AppWidgetProvider, חשוב לשים לב לשיקולים הבאים לגבי משך הזמן והעדיפות של עדכון הווידג'ט.

משך העדכון

ככלל, המערכת מאפשרת ל-broadcast receivers, שפועלים בדרך כלל בשרשור הראשי של האפליקציה, לפעול עד 10 שניות לפני שהיא מחשיבה אותם כלא מגיבים ומפעילה שגיאת Application Not Responding (ANR). כדי להימנע מחסימה של ה-main thread בזמן הטיפול בשידור, משתמשים בשיטה goAsync. אם לוקח יותר זמן לעדכן את הווידג'ט, כדאי לתזמן משימה באמצעות WorkManager.

Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.

מידע נוסף זמין במאמר בנושא שיקולי אבטחה ושיטות מומלצות.

העדיפות של העדכון

כברירת מחדל, שידורים – כולל שידורים שמתבצעים באמצעות AppWidgetProvider.onUpdate – פועלים כתהליכים ברקע. המשמעות היא שמשאבי מערכת עמוסים מדי עלולים לגרום לעיכוב בהפעלת מקלט השידור. כדי לתת עדיפות לשידור, צריך להגדיר אותו כתהליך בחזית.

לדוגמה, מוסיפים את הדגל Intent.FLAG_RECEIVER_FOREGROUND ל-Intent שמועבר אל PendingIntent.getBroadcast כשהמשתמש מקיש על חלק מסוים בווידג'ט.