Na tej stronie znajdziesz informacje o sprawdzonych metodach tworzenia bardziej zaawansowanego widżetu, który zapewni większą wygodę użytkownikom.
Optymalizacja aktualizacji treści widżetu
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żesz zaktualizować na 3 sposoby: w pełni, częściowo lub, w przypadku widżetu kolekcji, odświeżyć dane. Każdy z nich wiąże się z różnymi kosztami i konsekwencjami obliczeniowymi.
Poniżej opisujemy poszczególne typy aktualizacji i zamieszczamy odpowiednie fragmenty kodu.
Pełna aktualizacja: wywołaj funkcję
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 najbardziej czasochłonne z aktualizacji.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 połączenie nowegoRemoteViews
z dostarczonym wcześniejRemoteViews
. Ta metoda jest ignorowana, jeśli widżet nie otrzyma co najmniej 1 pełnego uaktualnienia za pomocą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);
Odświeżanie danych kolekcji: wywołaj
AppWidgetManager.notifyAppWidgetViewDataChanged
, aby unieważnić dane widoku kolekcji w widżecie. Spowoduje to uruchomienieRemoteViewsFactory.onDataSetChanged
. W międzyczasie w widżecie będą wyświetlane stare dane. Dzięki tej metodzie możesz bezpiecznie wykonywać kosztowne zadania w sposób synchroniczny.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);
Te metody możesz wywoływać z dowolnego miejsca w aplikacji, o ile ma ona ten sam identyfikator UID co odpowiadająca jej klasa AppWidgetProvider
.
Określanie częstotliwości aktualizowania widżetu
Widgety są okresowo aktualizowane w zależności od wartości podanej dla atrybutu updatePeriodMillis
. Widget może się aktualizować w reakcji na interakcję z użytkownikiem, aktualizacje rozsyłane do wielu urządzeń lub obie te czynności.
Aktualizuj okresowo
Częstotliwość okresowych aktualizacji możesz kontrolować, określając wartość elementu 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 jego aktualizacja trwa dłużej niż 10 sekund, rozważ alternatywy dla aktualizacji odbiornika transmisji opisane w następującej sekcji, ponieważ po 10 sekundach system uzna, że BroadcastReceiver
nie odpowiada.
updatePeriodMillis
nie obsługuje wartości krótszych niż 30 minut. Jeśli jednak chcesz wyłączyć okresowe aktualizacje, możesz ustawić wartość 0.
Możesz zezwolić użytkownikom na dostosowywanie częstotliwości aktualizacji w konfiguracji. Mogą na przykład chcieć, aby ticker akcji był aktualizowany 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 na podstawie 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 wybierzeszBroadcast
dlaPendingIntent
, możesz wybrać komentarz na pierwszym planie, aby nadaćBroadcastReceiver
priorytet.
Aktualizacja w odpowiedzi na zdarzenie transmisji
Przykładem transmitowanego wydarzenia, które wymaga aktualizacji widżetu, jest zrobienie zdjęcia przez użytkownika. W tym przypadku chcesz zaktualizować widget, gdy zostanie wykryte nowe zdjęcie.
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 Androidzie 7.0 (poziom interfejsu API 24) i Androidzie 8.0 (poziom interfejsu API 26) wprowadzono ograniczenia dotyczące transmisji danych, które uniemożliwiają rejestrowanie w plikach manifestu niejawnych transmisji danych (z pewnymi wyjątkami).
O czym należy pamiętać podczas aktualizowania widżetu z poziomu BroadcastReceiver
Jeśli widżet jest aktualizowany z poziomu BroadcastReceiver
, w tym AppWidgetProvider
, pamiętaj o tych kwestiach dotyczących czasu trwania i priorytetu aktualizacji widżetu.
Czas trwania aktualizacji
Zazwyczaj system pozwala odbiornikom transmisji, które zwykle działają w głównym wątku aplikacji, działać przez maksymalnie 10 sekund, zanim uzna je za niedziałające i wyświetli błąd Aplikacja nie odpowiada (ANR). Jeśli aktualizacja widżetu trwa dłużej, rozważ te opcje:
Zaplanuj zadanie za pomocą
WorkManager
.Daj odbiorcy więcej czasu, korzystając z metody
goAsync
. Dzięki temu odbiorcy będą wykonywać 30 sekund.
Więcej informacji znajdziesz w artykule Uwagi na temat bezpieczeństwa i sprawdzone metody.
Priorytet aktualizacji
Domyślnie transmisje (w tym te realizowane za pomocąAppWidgetProvider.onUpdate
) są wykonywane jako procesy w tle. Oznacza to, że przeciążone zasoby systemowe mogą powodować opóźnienie w wywoływaniu odbiornika. Aby nadawanie miało priorytet, ustaw je jako proces na pierwszym planie.
Możesz na przykład dodać do parametru Intent
przekazywanego do parametru PendingIntent.getBroadcast
flagę Intent.FLAG_RECEIVER_FOREGROUND
, która jest wywoływana, gdy użytkownik kliknie określony element widżetu.
Tworzenie dokładnych podglądów, które zawierają elementy dynamiczne
W tej sekcji znajdziesz wyjaśnienie zalecanego podejścia do wyświetlania wielu elementów w podglądzie podglądu widżetu z widokiem kolekcji, czyli widżetu, który używa tagu ListView
, GridView
lub StackView
.
Jeśli widżet używa jednego z tych widoków, tworzenie skalowanego podglądu przez bezpośrednie udostępnianie rzeczywistego układu widżetu pogarsza wrażenia użytkownika, gdy podgląd widżetu nie wyświetla żadnych elementów. Dzieje się tak, ponieważ dane widoku kolekcji są ustawiane dynamicznie w czasie wykonywania programu i wyglądają podobnie do obrazu przedstawionego na rysunku 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 widok kolekcji obiektów zastępczych z fałszywymi elementami. Możesz na przykład zasymulować obiekt ListView
, podając obiekt zastępczy LinearLayout
z kilkoma fałszywymi elementami listy.
Aby zilustrować przykład 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>
Podczas podawania atrybutu previewLayout
w metadanych AppWidgetProviderInfo
określ plik układu podglądu. Nadal określasz rzeczywisty układ widżetu dla atrybutu initialLayout
i używasz rzeczywistego układu widżetu podczas tworzenia elementu RemoteViews
w czasie wykonywania.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
Elementy listy złożone
Przykład w poprzedniej sekcji zawiera fałszywe elementy listy, ponieważ są to obiekty TextView
. Podanie fałszywych elementów może być trudniejsze, jeśli są to złożone układy.
Weź pod uwagę element listy zdefiniowany w elementach widget_list_item.xml
, który składa 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 elementy listy, możesz użyć układu kilka razy, ale spowoduje to, że wszystkie elementy będą identyczne. Aby przesłać 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>
Aby ustawić tekst, użyj tych atrybutów:
<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 tyle stylów, ile jest potrzebnych do wyświetlenia 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>
Zastosowanie stylów 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>