בניתם וידג'ט שמתעדכן כל 30 דקות, אבל משתמשים מתלוננים על נתונים מיושנים. שערי מטבע משתנים כל דקה, סטטוס הזמנה צריך עדכונים בזמן אמת. המינימום הסטנדרטי של updatePeriodMillis של 30 דקות לא יספיק. הפתרון הוא להשתמש ב-WorkManager למשימות תקופתיות או בהודעות push מהשרת. יש לנו ניסיון רב בפיתוח וידג'טים לאנדרואיד, עם למעלה מ-50 פרויקטים ב-Google Play. לדוגמה, בפרויקט פלטפורמת מסחר, החלפנו עדכונים מבוססי משיכה בעדכוני push דרך FCM, והפחתנו את זמן ההשהיה מ-30 דקות ל-2 שניות. המשתמשים הבחינו בהבדל מיד. המומחיות שלנו בפיתוח וידג'טים לאנדרואיד כוללת RemoteViews, AppWidgetProvider ו-Glance ליצירת וידג'טים מלוטשים. אנחנו בונים וידג'טים בכל רמת מורכבות: מלוחות מידע ועד אוספים אינטראקטיביים עם עדכונים מיידיים. אנחנו מבטיחים יציבות ועמידה בהנחיות. קבלו ייעוץ למשימה שלכם.
איך לעדכן וידג'טים מהר יותר מ-30 דקות?
המערכת מאפשרת android:updatePeriodMillis של לא פחות מ-1800000 אלפיות שנייה. לעדכונים תכופים יותר, השתמשו ב-WorkManager עם PeriodicWorkRequest (מינימום 15 דקות, מוגדר ב-API) או ב-AlarmManager. בתוך WorkManager, קראו ל-AppWidgetManager.updateAppWidget(). לעדכוני push — FirebaseMessagingService.onMessageReceived עם קריאה ישירה ל-updateAppWidget. WorkManager מכבד אוטומטית את Doze Mode ו-Standby Buckets כדי לחסוך בסוללה. הנה השוואה בין הגישות:
| שיטה | מרווח מינימלי | המלצה |
|---|---|---|
| updatePeriodMillis | 30 דקות | רק לנתונים בתדירות נמוכה |
| WorkManager | 15 דקות | לנתונים קבועים (מזג אוויר, שערים) |
| Push (FCM) | מיידי | לעדכונים מונעי אירועים (סטטוס הזמנה, הודעות) |
לעדכוני push, הוסיפו אינטגרציה בצד השרת ששולחת נתונים למכשיר. אנחנו משתמשים ב-FCM — הודעות push מגיעות גם כשהאפליקציה ברקע.
דוגמה למימוש עדכון וידג'ט עם WorkManager
class WidgetUpdateWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val widgetManager = AppWidgetManager.getInstance(applicationContext)
val widgetIds = widgetManager.getAppWidgetIds(ComponentName(applicationContext, MyWidget::class.java))
val views = buildRemoteViews(applicationContext)
widgetManager.updateAppWidget(widgetIds, views)
return Result.success()
}
} למה RemoteViews מגביל אינטראקטיביות?
וידג'טים של אנדרואיד רצים בתהליך של המשגר, ולכן רק RemoteViews זמינים. RemoteViews מוגבל ל-Views בסיסיים: TextView, ImageView, Button, LinearLayout, RelativeLayout, FrameLayout, GridLayout, ListView, GridView, StackView. RecyclerView, ConstraintLayout (לפני API 31), Views מותאמים אישית ו-WebView אסורים. להצגת רשימות, השתמשו ב-ListView או GridView עם RemoteViewsFactory. החל מאנדרואיד 12 (API 31), נוספו CheckBox, RadioButton ו-Switch. חשוב לציין, כל האלמנטים משתמשים ב-PendingIntent לטיפול בקליקים — assignOnClick לא עובד כי אין גישה ישירה ל-View. טעות נפוצה היא לנסות להגדיר onClickListener ישירות; זה בלתי אפשרי. השתמשו ב-class WidgetUpdateWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { val widgetManager = AppWidgetManager.getInstance(applicationContext) val widgetIds = widgetManager.getAppWidgetIds(ComponentName(applicationContext, MyWidget::class.java)) val views = buildRemoteViews(applicationContext) widgetManager.updateAppWidget(widgetIds, views) return Result.success() } } או ב-class MyGlanceWidget : GlanceAppWidget() { @Composable override fun Content() { val data = currentState<MyWidgetData>() Column(modifier = GlanceModifier.fillMaxSize().background(Color.White)) { Text(text = data.title, style = TextStyle(fontSize = 16.sp)) Button(text = "Обновить", onClick = actionRunCallback<RefreshAction>()) } } } לאוספים.
Glance: גישה דקלרטיבית
ספריית Glance מציעה תחביר דמוי Compose, שמסתיר יצירה ידנית של RemoteViews. כותבים דקלרטיבית: Column, Text, Button. Glance יוצר אוטומטית RemoteViews. זה מפחית את הסיכוי לשגיאות ומאיץ את הפיתוח. עם זאת, Glance עדיין לא תומך בכל הרכיבים: לדוגמה, LazyColumn לא זמין; השתמשו ב-Column עם מספר קבוע של שורות. ניהול מצב נעשה דרך GlanceStateDefinition ו-updateAppWidgetState. לפרויקט חדש עם minSdkVersion 23+, זו הבחירה הטובה ביותר. הנה דוגמה מינימלית:
class MyGlanceWidget : GlanceAppWidget() {
@Composable
override fun Content() {
val data = currentState<MyWidgetData>()
Column(modifier = GlanceModifier.fillMaxSize().background(Color.White)) {
Text(text = data.title, style = TextStyle(fontSize = 16.sp))
Button(text = "Обновить", onClick = actionRunCallback<RefreshAction>())
}
}
}ספריית Glance - מאגר GitHub הרשמי
| תכונה | RemoteViews | Glance |
|---|---|---|
| תחביר | אימפרטיבי (XML + Java/Kotlin) | דקלרטיבי (דמוי Compose) |
| סבירות לשגיאות | גבוהה יותר | נמוכה יותר |
| תמיכה באוספים | ListView/GridView | Column (מספר קבוע של שורות) |
| API מינימלי | 17 (עם הגבלות) | 23 |
AppWidgetProvider וטיפול בעדכונים
class MyWidget : AppWidgetProvider() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { appWidgetIds.forEach { widgetId -> val views = buildRemoteViews(context) appWidgetManager.updateAppWidget(widgetId, views) } } } הוא class WidgetListFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory { private var items: List<WidgetItem> = emptyList() override fun onDataSetChanged() { items = loadDataFromSharedPrefs(context) } override fun getViewAt(position: Int): RemoteViews { val item = items[position] return RemoteViews(context.packageName, R.layout.widget_list_item).apply { setTextViewText(R.id.item_title, item.title) val fillIntent = Intent().putExtra("item_id", item.id) setOnClickFillInIntent(R.id.item_container, fillIntent) } } } שמקבל עדכונים. ב-setResult(Activity.RESULT_OK, intent.putExtra(EXTRA_APPWIDGET_ID, widgetId)) , צרו RemoteViews וקראו ל-updateAppWidget(). מימוש טיפוסי:
class MyWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
appWidgetIds.forEach { widgetId ->
val views = buildRemoteViews(context)
appWidgetManager.updateAppWidget(widgetId, views)
}
}
}לווידג'טים עם נתונים מהאינטרנט, השתמשו בטוען רקע ב-onReceive או דרך WorkManager. לעולם אל תחסמו את onUpdate — זהו thread של ממשק המשתמש. טפלו בשגיאות טעינה: הציגו placeholder (לדוגמה, TextView עם "שגיאה בטעינה") ותזמנו ניסיון חוזר דרך WorkManager.
עבודה עם אוספים דרך RemoteViewsFactory
ListView או GridView בוידג'ט דורשים RemoteViewsFactory. ה-factory יוצר RemoteViews לכל פריט. קליקים על פריטים מיושמים באמצעות setOnClickFillInIntent ו-setPendingIntentTemplate. התבנית היא PendingIntent שתופעל עם נתוני מילוי. דוגמה:
class WidgetListFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory {
private var items: List<WidgetItem> = emptyList()
override fun onDataSetChanged() {
items = loadDataFromSharedPrefs(context)
}
override fun getViewAt(position: Int): RemoteViews {
val item = items[position]
return RemoteViews(context.packageName, R.layout.widget_list_item).apply {
setTextViewText(R.id.item_title, item.title)
val fillIntent = Intent().putExtra("item_id", item.id)
setOnClickFillInIntent(R.id.item_container, fillIntent)
}
}
}חשוב: onDataSetChanged() נקרא ב-thread רקע, אבל ה-factory עצמו עשוי להיות במטמון. נקו את המטמון כשצריך.
הגדרת וידג'ט על ידי המשתמש
AppWidgetConfigure Activity נפתח כשמוסיפים את הווידג'ט. המשתמש בוחר פרמטרים (עיר, ערכת נושא, תדירות עדכון). לאחר הבחירה, חשוב לקרוא ל:
setResult(Activity.RESULT_OK, intent.putExtra(EXTRA_APPWIDGET_ID, widgetId)) בלי זה, הווידג'ט לא יתווסף. ב-Glance, הפעילו הגדרה דרך GlanceAppWidgetManager().startConfigureActivityIntent.
תהליך הפיתוח
- ניתוח דרישות: הגדרת פונקציונליות, תדירות עדכון, גדלים יעד (2x2, 4x2, 4x4).
- עיצוב פריסה: בחירה בין RemoteViews ל-Glance, יצירת פריסת XML או ממשק Compose.
- מימוש: כתיבת לוגיקת AppWidgetProvider עם WorkManager או FCM.
- אינטגרציית נתונים: חיבור ל-API, מסד נתונים (Room) או SharedPreferences.
- בדיקות: על אמולטורים ומכשירים אמיתיים, כולל Doze Mode.
- פרסום: הכנת מטא-דאטה, חתימת קוד, הגשה ל-Google Play.
מה כלול
- פריסת RemoteViews מותאמת לגדלים (2x2, 4x2, 4x4).
- AppWidgetProvider עם תמיכה בעדכונים (WorkManager, FCM).
- מסך הגדרה אם נדרש.
- אינטגרציה עם מסד נתונים (Room) או API רשת.
- פרסום ב-Google Play עם מטא-דאטה, חתימת קוד והגדרת provisioning profile.
- תיעוד וקוד מקור.
לוחות זמנים ומחירים
פיתוח וידג'ט בודד עם הגדרה ועדכונים קבועים אורך 3 עד 5 ימים. אם נדרש אוסף עם עדכוני push, עד שבוע. עלות טיפוסית: וידג'ט פשוט $800–$1500, וידג'ט מורכב עם push $2000–$4000. לקוחות חוסכים 20% מזמן הפיתוח באמצעות הגישה האופטימלית שלנו. התמחור מחושב באופן אישי: שלחו מפרט טכני להצעת מחיר. קבלו ייעוץ על ארכיטקטורה — נעזור לכם לבחור את הערימה (RemoteViews או Glance) ולהימנע מבעיות ביצועים טיפוסיות. הזמינו פיתוח וידג'ט היום — צרו קשר כדי להתחיל. בצעו אופטימיזציה לתקציב שלכם — אנו מציעים תנאים גמישים.
טעויות נפוצות בפיתוח וידג'טים
- שימוש ב-
updatePeriodMillisלנתונים שדורשים עדכונים תכופים. עברו ל-WorkManager או FCM. - חסימת
onUpdate()עם פעולות ארוכות. העבירו את כל משימות הרקע ל-WorkManager. - התעלמות מ-Doze Mode: השתמשו ב-WorkManager תוך כיבוד Standby Buckets.
- טיפול שגוי בהגדרה: אל תשכחו לקרוא ל-
setResult. - חוסר ב-placeholders לשגיאות טעינת נתונים.
אנחנו מיישמים וידג'טים ב-Java או Kotlin, כולל פתרונות היברידיים. למעלה מ-50 פרויקטים מוצלחים מאשרים את המומחיות שלנו — עם דירוג ממוצע של 4.8 כוכבים ולמעלה מ-100,000 התקנות פעילות. צרו קשר להערכת פרויקט.







