Questa pagina spiega le pratiche consigliate per creare un widget più avanzato per una migliore esperienza utente.
Ottimizzazioni per l'aggiornamento dei contenuti del widget
L'aggiornamento dei contenuti dei widget può essere costoso dal punto di vista computazionale. Per risparmiare batteria, ottimizza il tipo, la frequenza e la tempistica degli aggiornamenti.
Tipi di aggiornamenti dei widget
Esistono tre modi per aggiornare un widget: un aggiornamento completo, un aggiornamento parziale e, nel caso di un widget raccolta, un aggiornamento dei dati. Ognuno ha costi di calcolo e conseguenze diversi.
Di seguito vengono descritti ciascun tipo di aggiornamento e forniti snippet di codice per ognuno.
Aggiornamento completo: chiama
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
per aggiornare completamente il widget.RemoteViews
fornito in precedenza viene sostituito con un nuovoRemoteViews
. Questo è l'aggiornamento più costoso in termini di calcolo.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);
Aggiornamento parziale: chiama
AppWidgetManager.partiallyUpdateAppWidget
per aggiornare parti del widget. In questo modo, il nuovoRemoteViews
viene unito alRemoteViews
fornito in precedenza. Questo metodo viene ignorato se un widget non riceve almeno un aggiornamento completo tramiteupdateAppWidget(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);
Aggiornamento dei dati della raccolta:chiama
AppWidgetManager.notifyAppWidgetViewDataChanged
per invalidare i dati di una visualizzazione della raccolta nel widget. Viene attivatoRemoteViewsFactory.onDataSetChanged
. Nel frattempo, i vecchi dati vengono visualizzati nel widget. Con questo metodo puoi eseguire in modo sicuro attività costose in modo sincrono.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);
Puoi chiamare questi metodi da qualsiasi punto dell'app, a condizione che l'app abbia lo stesso UID della classe AppWidgetProvider
corrispondente.
Determinare la frequenza di aggiornamento di un widget
I widget vengono aggiornati periodicamente a seconda del valore fornito per l'attributo
updatePeriodMillis
. Il widget può aggiornarsi in risposta all'interazione dell'utente, trasmettere
aggiornamenti o entrambe le cose.
Aggiornamenti periodici
Puoi controllare la frequenza dell'aggiornamento periodico specificando un valore per
AppWidgetProviderInfo.updatePeriodMillis
nel file XML appwidget-provider
. Ogni
aggiornamento attiva il metodo AppWidgetProvider.onUpdate()
, in cui puoi
inserire il codice per aggiornare il widget. Tuttavia, se il widget deve caricare i dati in modo asincrono o impiega più di 10 secondi per aggiornarsi, perché dopo 10 secondi il sistema considera un BroadcastReceiver
come non reattivo, prendi in considerazione le alternative per gli aggiornamenti del ricevitore di trasmissione descritte in una sezione successiva.
updatePeriodMillis
non supporta valori inferiori a 30 minuti. Tuttavia, se
vuoi disattivare gli aggiornamenti periodici, puoi specificare 0.
Puoi consentire agli utenti di regolare la frequenza degli aggiornamenti in una configurazione. Ad esempio, potrebbero voler aggiornare un ticker azionario ogni 15 minuti o solo quattro volte al giorno. In questo caso, imposta updatePeriodMillis
su 0 e utilizza
WorkManager
.
Aggiornamento in risposta a un'interazione dell'utente
Ecco alcuni modi consigliati per aggiornare il widget in base all'interazione dell'utente:
Da un'attività dell'app:chiama direttamente
AppWidgetManager.updateAppWidget
in risposta a un'interazione utente, ad esempio un tocco dell'utente.Da interazioni remote, ad esempio una notifica o un widget dell'app: costruisci un
PendingIntent
, quindi aggiorna il widget dall'Activity
, dall'Broadcast
o dall'Service
richiamato. Puoi scegliere la tua priorità. Ad esempio, se selezioni unBroadcast
perPendingIntent
, puoi scegliere una trasmissione in primo piano per dare la priorità aBroadcastReceiver
.
Aggiornamento in risposta a un evento di trasmissione
Un esempio di evento di trasmissione che richiede l'aggiornamento di un widget è quando l'utente scatta una foto. In questo caso, vuoi aggiornare il widget quando viene rilevata una nuova foto.
Puoi pianificare un job con JobScheduler
e specificare una trasmissione come
trigger utilizzando il
metodo
JobInfo.Builder.addTriggerContentUri
.
Puoi anche registrare un BroadcastReceiver
per la trasmissione, ad esempio,
ascoltando
ACTION_LOCALE_CHANGED
.
Tuttavia, poiché questa operazione consuma le risorse del dispositivo, utilizzala con cautela e ascolta
solo la trasmissione specifica. Con l'introduzione delle limitazioni
alle trasmissioni in Android
7.0 (livello API 24) e Android 8.0 (livello API 26), le app non possono registrare trasmissioni
implicite nei relativi manifest, con alcune
eccezioni.
Considerazioni sull'aggiornamento di un widget da un BroadcastReceiver
Se il widget viene aggiornato da un BroadcastReceiver
, incluso
AppWidgetProvider
, tieni presente le seguenti considerazioni relative alla
durata e alla priorità di un aggiornamento del widget.
Durata dell'aggiornamento
Di norma, il sistema consente ai ricevitori di trasmissione, che in genere vengono eseguiti nel thread principale dell'app, di essere eseguiti per un massimo di 10 secondi prima di considerarli non reattivi e di attivare un errore Applicazione non
risponde (ANR). Per evitare di bloccare il
thread principale durante la gestione della trasmissione, utilizza il
metodo goAsync
. Se l'aggiornamento del widget richiede
più tempo, valuta la possibilità di programmare un'attività
utilizzando WorkManager
.
Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.
Per saperne di più, consulta Considerazioni e best practice per la sicurezza.
Priorità dell'aggiornamento
Per impostazione predefinita, le trasmissioni, incluse quelle effettuate utilizzando
AppWidgetProvider.onUpdate
, vengono eseguite come processi in background. Ciò significa che
le risorse di sistema sovraccariche possono causare un ritardo nell'invocazione del
ricevitore di trasmissione. Per dare la priorità alla trasmissione, rendila un processo in primo piano.
Ad esempio, aggiungi il
flag Intent.FLAG_RECEIVER_FOREGROUND
a Intent
passato a PendingIntent.getBroadcast
quando l'utente
tocca una determinata parte del widget.
Creare anteprime accurate che includano elementi dinamici

Questa sezione illustra l'approccio consigliato per visualizzare più elementi in
un'anteprima del widget per un widget con una visualizzazione
raccolta, ovvero un widget che utilizza
ListView
, GridView
o StackView
.
Se il widget utilizza una di queste visualizzazioni, la creazione di un'anteprima scalabile fornendo direttamente il layout del widget effettivo peggiora l'esperienza quando l'anteprima del widget non mostra elementi. Ciò si verifica perché i dati della visualizzazione raccolta vengono impostati in modo dinamico in fase di runtime e sono simili all' immagine mostrata nella Figura 1.
Per visualizzare correttamente le anteprime dei widget con visualizzazioni di raccolta nel selettore di widget, ti consigliamo di mantenere un file di layout separato designato solo per l'anteprima. Questo file di layout separato include il layout effettivo del widget e una
visualizzazione della raccolta segnaposto con elementi fittizi. Ad esempio, puoi imitare un
ListView
fornendo un segnaposto LinearLayout
con diversi elementi
di elenco fittizi.
Per illustrare un esempio per un ListView
, inizia con un file di layout separato:
// 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>
Specifica il file di layout dell'anteprima quando fornisci l'attributo previewLayout
dei metadati AppWidgetProviderInfo
. Continua a specificare il layout effettivo del widget
per l'attributo initialLayout
e a utilizzare il layout effettivo del widget quando
costruisci un RemoteViews
in fase di runtime.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
Voci di elenco complesse
L'esempio nella sezione precedente fornisce voci di elenco fittizie, perché le voci di elenco sono oggetti TextView
. Può essere
più complesso fornire elementi falsi se gli elementi sono layout complessi.
Considera una voce di elenco definita in widget_list_item.xml
e composta da
due oggetti 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>
Per fornire voci di elenco fittizie, puoi includere il layout più volte, ma in questo modo ogni voce di elenco sarà identica. Per fornire elementi di elenco unici, segui questa procedura:
Crea un insieme di attributi per i valori di testo:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
Utilizza questi attributi per impostare il testo:
<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>
Crea tutti gli stili necessari per l'anteprima. Ridefinisci i valori in ogni stile:
<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>
Applica gli stili agli elementi fittizi nel layout di anteprima:
<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>