Na tej stronie opisujemy zalecane metody tworzenia bardziej zaawansowanych widżetów, które zwiększają wygodę użytkowników.
Optymalizacje w zakresie aktualizowania zawartości widżetów
Aktualizowanie zawartości widżetu może być kosztowne pod względem obliczeń. Aby zmniejszyć zużycie baterii, zoptymalizuj typ, częstotliwość i czas aktualizacji.
Rodzaje aktualizacji widżetów
Widżet można aktualizować na 3 sposoby: pełna aktualizacja, aktualizacja częściowa oraz, w przypadku widżetu kolekcji, odświeżenie danych. Każdy z nich niesie ze sobą inne koszty obliczeniowe i konsekwencje.
Poniżej opisujemy poszczególne typy aktualizacji i przedstawiamy fragmenty kodu dla każdego z nich.
Pełna aktualizacja: wywołaj
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
, aby w pełni zaktualizować widżet. Spowoduje to zastąpienie wcześniej podanej wartościRemoteViews
nowym elementemRemoteViews
. To najdroższa aktualizacja pod względem obliczeń.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);
Częściowa aktualizacja: wywołaj
AppWidgetManager.partiallyUpdateAppWidget
, by zaktualizować części widżetu. Spowoduje to scalenie nowegoRemoteViews
z wcześniej podanymRemoteViews
. Ta metoda jest ignorowana, jeśli widżet nie otrzyma co najmniej jednej pełnej aktualizacji doupdateAppWidget(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);
Odświeżanie danych kolekcji: wywołaj
AppWidgetManager.notifyAppWidgetViewDataChanged
, aby unieważnić dane widoku kolekcji w widżecie. Spowoduje to aktywowanieRemoteViewsFactory.onDataSetChanged
. Do tego czasu w widżecie wyświetlane są stare dane. Dzięki tej metodzie możesz bezpiecznie wykonywać drogie zadania synchronicznie.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);
Możesz wywoływać te metody z dowolnego miejsca w aplikacji, jeśli ma ona taki sam identyfikator UID co odpowiednia klasa AppWidgetProvider
.
Określanie częstotliwości aktualizowania widżetu
Widżety są aktualizowane okresowo w zależności od wartości atrybutu updatePeriodMillis
. Widżet może się aktualizować w odpowiedzi na interakcję użytkownika, komunikaty lub jedno i drugie.
Aktualizuj okresowo
Możesz kontrolować częstotliwość okresowych aktualizacji, określając wartość AppWidgetProviderInfo.updatePeriodMillis
w pliku XML appwidget-provider
. Każda aktualizacja uruchamia metodę AppWidgetProvider.onUpdate()
, w której możesz umieścić kod aktualizujący widżet. Jeśli jednak widżet musi wczytywać dane asynchronicznie lub aktualizować się dłużej niż 10 sekund, po 10 sekundach system uzna, że BroadcastReceiver
nie odpowiada, dlatego rozważ zastosowanie alternatywnych aktualizacji odbiornika opisanych w następnej sekcji.
updatePeriodMillis
nie obsługuje wartości krótszych niż 30 minut. Jeśli jednak chcesz wyłączyć okresowe aktualizacje, możesz wpisać 0.
Możesz zezwolić użytkownikom na dostosowywanie częstotliwości aktualizacji w konfiguracji. Przykładowo mogą chcieć, aby pasek giełdowy aktualizował się co 15 minut lub tylko 4 razy dziennie. W takim przypadku ustaw updatePeriodMillis
na 0 i użyj WorkManager
.
Aktualizacja w odpowiedzi na interakcję użytkownika
Oto kilka zalecanych sposobów aktualizowania widżetu w zależności od interakcji użytkownika:
Z poziomu aktywności w aplikacji: wywołaj bezpośrednio
AppWidgetManager.updateAppWidget
w odpowiedzi na interakcję użytkownika, np. kliknięcie.Z interakcji zdalnych, takich jak powiadomienie lub widżet aplikacji: utwórz
PendingIntent
, a potem zaktualizuj widżet z wywołanychActivity
,Broadcast
lubService
. Możesz wybrać własny priorytet. Jeśli na przykład wybierzesz poleBroadcast
dla elementuPendingIntent
, możesz wybrać transmisję na pierwszym planie, aby nadać jej priorytetBroadcastReceiver
.
Aktualizacja w odpowiedzi na transmitowane wydarzenie
Przykładem transmitowanego wydarzenia, które wymaga aktualizacji widżetu, jest zrobienie zdjęcia przez użytkownika. W tej sytuacji możesz zaktualizować widżet po wykryciu nowego zdjęcia.
Możesz zaplanować zadanie za pomocą funkcji JobScheduler
i określić transmisję jako aktywator przy użyciu metody JobInfo.Builder.addTriggerContentUri
.
Możesz również zarejestrować adres BroadcastReceiver
na potrzeby transmisji, na przykład nasłuchiwać ACTION_LOCALE_CHANGED
.
Ponieważ jednak zużywa to zasoby urządzenia, korzystaj z tej funkcji ostrożnie i nasłuchuj tylko konkretnego komunikatu. W związku z wprowadzeniem ograniczeń transmisji na Androidzie 7.0 (poziom interfejsu API 24) i Androidzie 8.0 (poziom interfejsu API 26) aplikacje nie mogą rejestrować niejawnych komunikatów w plikach manifestu (z pewnymi wyjątkami).
Uwagi na temat aktualizowania widżetu z BroadcastReceiver
Jeśli widżet jest aktualizowany z elementu BroadcastReceiver
, w tym z elementu AppWidgetProvider
, pamiętaj o tych kwestiach związanych z czasem trwania i priorytetem aktualizacji widżetu.
Czas trwania aktualizacji
Z reguły system zezwala odbiornikom, które zwykle działają w głównym wątku aplikacji, przez maksymalnie 10 sekund, zanim uzna, że nie odpowiada, i wywoła błąd Aplikacja nie odpowiada (ANR). Jeśli aktualizacja widżetu trwa dłużej, rozważ inne możliwości:
Zaplanuj zadanie za pomocą funkcji
WorkManager
.Zapewnij odbiorcy więcej czasu, używając metody
goAsync
. Dzięki temu odbiorcy będą wykonywać 30 sekund.
Więcej informacji znajdziesz w artykule Kwestie związane z bezpieczeństwem i sprawdzone metody.
Priorytet aktualizacji
Domyślnie transmisje – w tym te przeprowadzone za pomocą AppWidgetProvider.onUpdate
– są uruchamiane w tle. Oznacza to, że przeciążone zasoby systemowe mogą powodować opóźnienie w wywoływaniu odbiornika. Aby nadać transmisji priorytetową, ustaw ją jako proces na pierwszym planie.
Na przykład dodaj flagę Intent.FLAG_RECEIVER_FOREGROUND
do elementu Intent
przekazywanego do PendingIntent.getBroadcast
, gdy użytkownik kliknie określoną część widżetu.
Tworzenie dokładnych podglądów z uwzględnieniem elementów dynamicznych
W tej sekcji opisujemy zalecane podejście do wyświetlania wielu elementów w podglądzie widżetu z widokiem kolekcji – czyli w widżetach używających ListView
, GridView
lub StackView
.
Jeśli Twój widżet korzysta z jednego z tych widoków, utworzenie skalowalnego podglądu przez bezpośrednie podanie rzeczywistego układu widżetu pogarsza komfort korzystania, gdy podgląd widżetu nie wyświetla żadnych elementów. Dzieje się tak, ponieważ dane widoku kolekcji są ustawiane dynamicznie w czasie działania i wyglądają podobnie jak na ilustracji 1.
Aby podgląd widżetów z widokami kolekcji był prawidłowo wyświetlany w selektorze widżetów, zalecamy zachowanie osobnego pliku układu przeznaczonego tylko na podgląd. Ten oddzielny plik układu zawiera rzeczywisty układ widżetu i zastępczy widok kolekcji z fałszywymi elementami. Możesz na przykład imitować element ListView
, podając obiekt zastępczy LinearLayout
z kilkoma fałszywymi elementami listy.
Aby pokazać przykład dla elementu ListView
, zacznij od osobnego pliku układu:
// 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>
Określ plik układu podglądu, podając atrybut previewLayout
metadanych AppWidgetProviderInfo
. Nadal możesz określić rzeczywisty układ widżetu dla atrybutu initialLayout
i używać rzeczywistego układu widżetu podczas tworzenia obiektu RemoteViews
w czasie działania.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
Złożone elementy listy
Przykład w poprzedniej sekcji przedstawia fałszywe elementy listy, bo elementy listy są obiektami TextView
. Dostarczenie fałszywych elementów może być bardziej skomplikowane, jeśli elementy mają złożone układy.
Weźmy pod uwagę element listy zdefiniowany w zasadzie widget_list_item.xml
i składający się z 2 obiektów 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>
Aby podać fałszywe pozycje listy, możesz dodać układ kilka razy, ale wtedy każdy element listy będzie taki sam. Aby podać unikalne elementy listy, wykonaj te czynności:
Utwórz zestaw atrybutów dla wartości tekstowych:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
Użyj tych atrybutów, aby ustawić tekst:
<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>
Utwórz dowolną liczbę stylów do podglądu. Zdefiniuj ponownie wartości w każdym stylu:
<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>
Zastosuj style do fałszywych elementów w układzie podglądu:
<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>