Na tej stronie znajdziesz informacje o sprawdzonych metodach tworzenia bardziej zaawansowanego widżetu, który zapewni użytkownikom większą wygodę.
Optymalizacja aktualizacji treści widżetu
Aktualizowanie treści widżetu może być kosztowne pod względem zasobów obliczeniowych. Aby oszczędzać baterię, 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 przesłanego plikuRemoteViews
nowym plikiemRemoteViews
. 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łanie
AppWidgetManager.partiallyUpdateAppWidget
służy do aktualizowania 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 metodę
AppWidgetManager.notifyAppWidgetViewDataChanged
, aby unieważnić dane widoku kolekcji na 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);
Możesz wywoływać te metody z dowolnego miejsca w aplikacji, o ile ma ona ten sam identyfikator UID co odpowiednia klasa AppWidgetProvider
.
Określanie częstotliwości odświeżania 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 działania użytkownika, aktualizacje wysyłane przez inne urządzenia lub na oba te czynniki.
Aktualizuj okresowo
Częstotliwość aktualizacji okresowych możesz kontrolować, określając wartość elementu AppWidgetProviderInfo.updatePeriodMillis
w pliku XML appwidget-provider
. Każda aktualizacja powoduje wywołanie metody AppWidgetProvider.onUpdate()
, w której możesz umieścić kod służący do aktualizowania widżetu. 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 zmianę częstotliwości aktualizacji w ramach konfiguracji. Mogą na przykład chcieć, aby ticker akcji był aktualizowany co 15 minut lub tylko 4 razy dziennie. W takim przypadku ustaw wartość updatePeriodMillis
na 0, a zamiast niej użyj operatora WorkManager
.
Aktualizacja w odpowiedzi na interakcję użytkownika
Oto kilka zalecanych sposobów aktualizowania widżetu na podstawie interakcji użytkownika:
Z aktywności aplikacji: wywołaj bezpośrednio funkcję
AppWidgetManager.updateAppWidget
w odpowiedzi na działanie użytkownika, np. kliknięcie.Z interakcji zdalnych, takich jak powiadomienie lub widżet aplikacji: utwórz
PendingIntent
, a następnie zaktualizuj widżet z wywołanegoActivity
,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 zdarzenia przesyłania strumieniowego, które wymaga zaktualizowania 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 użyć jako wyzwalacza transmisji strumieniowej, używając metody JobInfo.Builder.addTriggerContentUri
.
Możesz też zarejestrować BroadcastReceiver
dla transmisji, np.
słuchanie ACTION_LOCALE_CHANGED
.
Ponieważ jednak funkcja ta zużywa zasoby urządzenia, należy z niej korzystać ostrożnie i słuchać tylko konkretnego programu. 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 mogą wykonywać operacje przez 30 sekund.
Więcej informacji znajdziesz w artykule Uwagi i sprawdzone metody dotyczące bezpieczeństwa.
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 systemu mogą spowodować opóźnienie wywołania odbiornika transmisji. Aby nadawanie miało wyższy 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 zalecane podejście do wyświetlania wielu elementów w podglądzie 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 w selektorze widżetów prawidłowo wyświetlały się podglądy widżetów z widokami kolekcji, zalecamy utrzymanie osobnego pliku układu przeznaczonego tylko do podglądu. 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>