توضّح هذه الصفحة الممارسات الموصى بها لإنشاء أداة أكثر تقدمًا لتحسين تجربة المستخدم.
تحسينات لتحديث محتوى التطبيق المصغّر
قد يكون تحديث محتوى التطبيق المصغّر مُكلفًا من الناحية الحسابية. ولتوفير استهلاك البطارية، عليك تحسين نوع التحديث ومعدّل تكراره وتوقيته.
أنواع تحديثات التطبيقات المصغّرة
هناك ثلاث طرق لتحديث التطبيق المصغَّر: تحديث كامل، وتحديث جزئي، وتحديث البيانات في حالة استخدام أداة المجموعة. لكل منها تكاليف حسابية وتبعات مختلفة.
في ما يلي وصف لكل نوع تحديث، كما يوفر مقتطفات رمز لكل نوع.
تحديث كامل: يمكنك الاتصال بالرقم
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);
يمكنك طلب هذه الطرق من أي مكان في تطبيقك، طالما أنّ التطبيق يحتوي على
المعرّف الفريد نفسه لفئة AppWidgetProvider
المقابلة.
تحديد عدد مرات تحديث التطبيق المصغّر
يتم تعديل التطبيقات المصغّرة بشكل دوري استنادًا إلى القيمة المقدّمة للسمة
updatePeriodMillis
. ويمكن تحديث الأداة استجابةً لتفاعل المستخدم أو لبث التحديثات أو لكليهما.
التحديث بصفة دورية
يمكنك التحكّم في معدّل تكرار التعديلات الدورية من خلال تحديد قيمة
AppWidgetProviderInfo.updatePeriodMillis
في ملف appwidget-provider
XML. يؤدي كل
تحديث إلى ظهور الإجراء AppWidgetProvider.onUpdate()
، وهو المكان الذي يمكنك فيه وضع الرمز لتحديث الأداة. ومع ذلك، ننصحك بالاطّلاع على بدائل تحديثات مستقبل البث الموضحة في القسم التالي إذا كانت الأداة بحاجة إلى تحميل البيانات بشكل غير متزامن أو إذا استغرق تحديثها أكثر من 10 ثوانٍ، لأن النظام يعتبر BroadcastReceiver
غير مستجيب بعد 10 ثوانٍ.
لا يسمح updatePeriodMillis
باستخدام القيم التي تقلّ عن 30 دقيقة. ومع ذلك، إذا كنت تريد إيقاف التحديثات الدورية، يمكنك تحديد 0.
يمكنك السماح للمستخدمين بتعديل معدل تكرار التعديلات في الإعدادات. على سبيل المثال، قد يرغب في تحديث مؤشر سهم كل 15 دقيقة أو أربع مرات فقط في اليوم. في هذه الحالة، اضبط السمة updatePeriodMillis
على 0 واستخدِم
WorkManager
بدلاً من ذلك.
التعديل استجابةً لتفاعل مستخدم
في ما يلي بعض الطرق المُقترَحة لتحديث الأداة استنادًا إلى تفاعل المستخدم:
من نشاط التطبيق: يمكنك استدعاء
AppWidgetManager.updateAppWidget
مباشرةً استجابةً لتفاعل مستخدم، مثل نقرة المستخدم.من التفاعلات عن بُعد، مثل إشعار أو تطبيق مصغّر: أنشئ
PendingIntent
، ثم حدِّث التطبيق المصغّر من العناصر التي تم استدعاؤهاActivity
أوBroadcast
أوService
. ويمكنك اختيار أولويتك الخاصة. على سبيل المثال، إذا اخترتBroadcast
لـPendingIntent
، يمكنك اختيار بث في المقدّمة لمنح الأولويةBroadcastReceiver
.
التعديل استجابةً لحدث بث
مثال على حدث البث الذي يتطلب أداة للتحديث هو عندما يلتقط المستخدم صورة. في هذه الحالة، تريد تحديث الأداة عند اكتشاف صورة جديدة.
يمكنك جدولة مهمة باستخدام JobScheduler
وتحديد البث كعامل التفعيل باستخدام الطريقة
JobInfo.Builder.addTriggerContentUri
.
يمكنك أيضًا تسجيل BroadcastReceiver
للبث، على سبيل المثال،
الاستماع إلى
ACTION_LOCALE_CHANGED
.
مع ذلك، وبما أنّ هذا الإجراء يستهلك موارد الجهاز، استخدِمه بعناية واستمِع فقط إلى البث المحدّد. ومع طرح قيود البث في Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات) وAndroid 8.0 (المستوى 26 لواجهة برمجة التطبيقات)، لن تتمكّن التطبيقات من تسجيل عمليات البث الضمنية في بياناتها، مع بعض الاستثناءات.
نقاط يجب أخذها في الاعتبار عند تحديث تطبيق مصغّر من جهاز BroadcastRecipient.
إذا تم تحديث التطبيق المصغَّر من BroadcastReceiver
، بما في ذلك
AppWidgetProvider
، يجب الانتباه إلى الاعتبارات التالية المتعلقة
بمدة تحديث التطبيق المصغَّر وأولويته.
مدة التحديث
كقاعدة عامة، يسمح النظام لأجهزة استقبال البث، التي تعمل عادةً في سلسلة التعليمات الرئيسية للتطبيق، بتشغيلها لمدة تصل إلى 10 ثوانٍ قبل اعتبارها غير مستجيبة وظهور خطأ التطبيق لا يستجيب (ANR). إذا استغرق تحديث الأداة وقتًا أطول، يمكنك التفكير في البدائل التالية:
جدولة مهمة باستخدام
WorkManager
عليك منح المستلِم المزيد من الوقت باستخدام الطريقة
goAsync
. يتيح ذلك تنفيذ أجهزة الاستقبال لمدة 30 ثانية.
راجِع اعتبارات الأمان وأفضل الممارسات للحصول على مزيد من المعلومات.
أولوية التحديث
يتم تلقائيًا تنفيذ عمليات البث، بما في ذلك عمليات البث التي يتم إجراؤها باستخدام
AppWidgetProvider.onUpdate
، كعمليات في الخلفية. ويعني هذا أنّ تحميل موارد النظام قد يؤدي إلى تأخير في استدعاء استقبال البث. لمنح الأولوية للبث، اجعله عملية تعمل في المقدّمة.
على سبيل المثال، يمكنك إضافة علامة
Intent.FLAG_RECEIVER_FOREGROUND
إلى Intent
الذي تم ضبطه على PendingIntent.getBroadcast
عندما ينقر المستخدم
على جزء معيّن من الأداة.
إنشاء معاينات دقيقة تتضمن عناصر ديناميكية
![](https://developer.android.com/static/images/appwidgets/missing-list.png?authuser=1&hl=ar)
يوضّح هذا القسم النهج الموصى به لعرض عناصر متعددة في معاينة تطبيق مصغّر لأداة تتضمن طريقة عرض المجموعة، وهي أداة تستخدم ListView
أو GridView
أو StackView
.
إذا كان التطبيق المصغّر يستخدم إحدى طرق العرض هذه، سيؤدي إنشاء معاينة قابلة للتطوير من خلال توفير التنسيق الفعلي للأداة مباشرةً إلى خفض مستوى التجربة عندما لا تعرض معاينة الأداة أي عناصر. ويحدث ذلك بسبب تعيين بيانات عرض المجموعة ديناميكيًا في وقت التشغيل، وتبدو مشابهة للصورة الموضحة في الشكل 1.
لعرض معاينات الأدوات ذات طرق عرض المجموعات بشكل صحيح في منتقي الأدوات، نوصي بالاحتفاظ بملف تنسيق منفصل مخصص للمعاينة فقط. يتضمن ملف التخطيط المنفصل هذا تخطيط الأداة الفعلي
وعرض مجموعة العناصر النائبة مع العناصر الزائفة. على سبيل المثال، يمكنك محاكاة ListView
من خلال توفير عنصر نائب LinearLayout
يحتوي على عدة عناصر قائمة مزيفة.
لتوضيح مثال لـ ListView
، ابدأ بملف تنسيق منفصل:
// res/layout/widget_preview.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_background"
android:orientation="vertical">
// Include the actual widget layout that contains ListView.
<include
layout="@layout/widget_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// The number of fake items you include depends on the values you provide
// for minHeight or targetCellHeight in the AppWidgetProviderInfo
// definition.
<TextView android:text="@string/fake_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
<TextView android:text="@string/fake_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
</LinearLayout>
حدِّد ملف تنسيق المعاينة عند توفير السمة previewLayout
لبيانات AppWidgetProviderInfo
الوصفية. سيظل بإمكانك تحديد تنسيق التطبيق المصغّر للسمة initialLayout
واستخدام التنسيق الفعلي للأداة عند إنشاء RemoteViews
في وقت التشغيل.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
عناصر قائمة معقدة
يقدّم المثال الوارد في القسم السابق عناصر قائمة مزيفة، لأنّ عناصرها هي كائنات TextView
. قد يكون توفير عناصر مزيفة
أكثر تعقيدًا إذا كانت العناصر تخطيطات معقدة.
يمكنك استخدام عنصر قائمة محدّد في widget_list_item.xml
ويتكوّن من كائنَين من النوع TextView
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
لتوفير عناصر قائمة مزيفة، يمكنك تضمين التخطيط عدة مرات، ولكن هذا يؤدي إلى أن يكون كل عنصر من عناصر القائمة متطابقًا. لتقديم عناصر قائمة فريدة، اتّبِع الخطوات التالية:
أنشئ مجموعة من السمات للقيم النصية:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
استخدِم السمات التالية لضبط النص:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetTitle" /> <TextView android:id="@id/content" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetContent" /> </LinearLayout>
أنشئ العدد المطلوب من الأنماط للمعاينة. أعد تحديد القيم في كل نمط:
<resources> <style name="Theme.Widget.ListItem"> <item name="widgetTitle"></item> <item name="widgetContent"></item> </style> <style name="Theme.Widget.ListItem.Preview1"> <item name="widgetTitle">Fake Title 1</item> <item name="widgetContent">Fake content 1</item> </style> <style name="Theme.Widget.ListItem.Preview2"> <item name="widgetTitle">Fake title 2</item> <item name="widgetContent">Fake content 2</item> </style> </resources>
يمكنك تطبيق الأنماط على العناصر الزائفة في تنسيق المعاينة:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ...> <include layout="@layout/widget_view" ... /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview1" /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview2" /> </LinearLayout>