إنشاء تطبيق مصغّر متقدّم

توضّح هذه الصفحة الممارسات المقترَحة لإنشاء تطبيق مصغّر أكثر تقدّمًا لتوفير تجربة أفضل للمستخدم.

تحسينات لتعديل محتوى التطبيقات المصغّرة

قد يكون تعديل محتوى التطبيقات المصغّرة ذا تكلفة حسابية عالية. ولتوفير استهلاك البطارية، عليك تحسين نوع التحديث ومعدّل تكراره وتوقيته.

أنواع تحديثات التطبيقات المصغّرة

هناك ثلاث طرق لتعديل التطبيق المصغّر: تعديل كامل وتعديل جزئي، وفي حال تطبيق مصغّر للمجموعة، تحديث البيانات. ولكل منها تكاليف حسابية ونتائج مختلفة.

في ما يلي وصف لكل نوع تحديث، كما يوفر مقتطفات رمز لكل نوع.

  • التحديث الكامل: اتصل على 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 .

يمكنك أيضًا تسجيل BroadcastReceiver للبث، على سبيل المثال، الاستماع إلى ACTION_LOCALE_CHANGED. ومع ذلك، بما أنّ هذه الميزة تستهلك موارد الجهاز، يُرجى استخدامها بحذر والاستماع إلى البث المحدّد فقط. ومع طرح قيود البث في Android 7.0 (المستوى 24 لواجهة برمجة التطبيقات) وAndroid 8.0 (المستوى 26 لواجهة برمجة التطبيقات)، لن تتمكّن التطبيقات من تسجيل عمليات البث الضمنية في بياناتها، مع بعض الاستثناءات.

الاعتبارات الواجب مراعاتها عند تعديل تطبيق مصغّر من BroadcastReceiver

إذا تم تعديل التطبيق المصغّر من BroadcastReceiver، بما في ذلك AppWidgetProvider، يجب مراعاة النقاط التالية بشأن مدة تعديل التطبيق المصغّر وأولويته.

مدة التحديث

وكقاعدة عامة، يسمح النظام لأجهزة استقبال البث، التي تعمل عادةً في سلسلت الرسائل الأساسية للتطبيق، بالعمل لمدة تصل إلى 10 ثوانٍ قبل اعتبارها غير مستجيبة والتسبب في خطأ "التطبيق لا يستجيب". إذا استغرق تعديل التطبيق المصغّر وقتًا أطول، ننصحك باتّباع الخطوات التالية:

  • حدِّد موعدًا لمهمة باستخدام WorkManager.

  • يمكنك منح المستلِم المزيد من الوقت باستخدام الطريقة goAsync. يتيح ذلك للمستلمين تنفيذ الإجراء لمدة 30 ثانية.

راجِع اعتبارات الأمان وأفضل الممارسات للحصول على مزيد من المعلومات.

أولوية التحديث

يتم تشغيل عمليات البث، بما في ذلك تلك التي يتم إجراؤها باستخدام AppWidgetProvider.onUpdate، كعمليات في الخلفية تلقائيًا. وهذا يعني أنّه يمكن أن تؤدي موارد النظام المثقلة بالتحميل إلى تأخير في استدعاء معالج البث. لمنح البث الأولوية، اجعله عملية في المقدّمة.

على سبيل المثال، يمكنك إضافة علامة Intent.FLAG_RECEIVER_FOREGROUND إلى Intent الذي تم ضبطه على PendingIntent.getBroadcast عندما ينقر المستخدم على جزء معيّن من الأداة.

إنشاء معاينات دقيقة تتضمّن عناصر ديناميكية

الشكل 1: معاينة التطبيق المصغّر لا تعرض أي عناصر قائمة.

يوضّح هذا القسم النهج الموصى به لعرض عناصر متعددة في معاينة تطبيق مصغّر لأداة تتضمن طريقة عرض المجموعة، وهي أداة تستخدم 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>

لتقديم عناصر قائمة مزيّفة، يمكنك تضمين التنسيق عدة مرات، ولكن يؤدي ذلك إلى أن يكون كل عنصر في القائمة متطابقًا. لتقديم عناصر قائمة فريدة، اتّبِع الخطوات التالية:

  1. أنشئ مجموعة من السمات لقيم النصوص:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. استخدِم السمات التالية لضبط النص:

    <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>
    
  3. أنشئ العدد الذي تريده من الأنماط للمعاينة. أعِد تحديد القيم في كل نمط:

    <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>
    
  4. طبِّق الأنماط على العناصر المزيّفة في تنسيق المعاينة:

    <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>