Questa pagina spiega le pratiche consigliate per la creazione di un widget più avanzato al fine di 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 del calcolo. Per risparmiare batteria, ottimizza il tipo, la frequenza e i tempi di aggiornamento.
Tipi di aggiornamenti dei widget
Esistono tre modi per aggiornare un widget: un aggiornamento completo, un aggiornamento parziale e, nel caso di un widget di raccolta, un aggiornamento dei dati. Ognuno ha costi e ramificazioni di calcolo diversi.
Di seguito viene descritto ogni tipo di aggiornamento e vengono forniti snippet di codice per ciascuno.
Aggiornamento completo: chiama
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
per aggiornare completamente il widget. Questo valore sostituisce l'elementoRemoteViews
fornito in precedenza con un nuovoRemoteViews
. Si tratta dell'aggiornamento più costoso dal punto di vista del 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. 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 vista raccolta nel widget. Questo attivaRemoteViewsFactory.onDataSetChanged
. Nel frattempo, i dati precedenti vengono visualizzati nel widget. Con questo metodo puoi eseguire in sicurezza 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, agli aggiornamenti degli annunci o a entrambe.
Aggiorna periodicamente
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()
, dove puoi
inserire il codice per aggiornare il widget. Tuttavia, prendi in considerazione le alternative per gli aggiornamenti del ricevitore di trasmissione descritte in una sezione che segue se il widget deve caricare i dati in modo asincrono o se l'aggiornamento richiede più di 10 secondi, perché dopo 10 secondi il sistema considera BroadcastReceiver
non reattivo.
updatePeriodMillis
non supporta valori inferiori a 30 minuti. Tuttavia, se vuoi disabilitare gli aggiornamenti periodici, puoi specificare 0.
Puoi consentire agli utenti di regolare la frequenza degli aggiornamenti in una configurazione. Ad esempio, potrebbero volere che un codice di borsa si aggiorni ogni 15 minuti o solo quattro volte al giorno. In questo caso, imposta updatePeriodMillis
su 0 e utilizza invece
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 dell'utente, ad esempio un tocco.Dalle interazioni remote, ad esempio una notifica o un widget dell'app: crea un
PendingIntent
, quindi aggiorna il widget dall'elementoActivity
,Broadcast
oService
richiamato. Puoi scegliere la tua priorità. Ad esempio, se selezioni un valoreBroadcast
perPendingIntent
, puoi scegliere una trasmissione in primo piano per assegnare 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, vorrai aggiornare il widget quando viene rilevata una nuova foto.
Puoi pianificare un job con JobScheduler
e specificare una trasmissione come
attivatore utilizzando il metodo
JobInfo.Builder.addTriggerContentUri
.
Puoi anche registrare un BroadcastReceiver
per la trasmissione, ad esempio
l'ascolto di
ACTION_LOCALE_CHANGED
.
Tuttavia, poiché consuma risorse del dispositivo, usalo con cautela e ascolta solo la trasmissione specifica. Con l'introduzione delle limitazioni di trasmissione in Android 7.0 (livello API 24) e Android 8.0 (livello API 26), le app non possono registrare trasmissioni implicite nei file manifest, con alcune eccezioni.
Considerazioni sull'aggiornamento di un widget da un BroadcastReceiver
Se il widget viene aggiornato da un BroadcastReceiver
, tra cui
AppWidgetProvider
, tieni presente le seguenti considerazioni relative alla
durata e alla priorità di un aggiornamento del widget.
Durata dell'aggiornamento
Come regola, il sistema consente ai broadcast receiver, che di solito vengono eseguiti nel thread principale dell'app, di eseguire fino a 10 secondi prima di considerare non rispondenti e di attivare un errore L'applicazione non risponde (ANR). Se l'aggiornamento del widget richiede più tempo, prendi in considerazione le seguenti alternative:
Pianifica un'attività utilizzando
WorkManager
.Concedi più tempo al destinatario con il metodo
goAsync
. In questo modo i ricevitori vengono eseguiti per 30 secondi.
Per ulteriori informazioni, consulta Considerazioni sulla sicurezza e best practice.
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 sovraccaricate possono causare un ritardo nella chiamata del ricevitore di broadcast. Per assegnare la priorità alla trasmissione, impostala come processo in primo piano.
Ad esempio, aggiungi il flag Intent.FLAG_RECEIVER_FOREGROUND
al Intent
passato a PendingIntent.getBroadcast
quando l'utente tocca una determinata parte del widget.
Crea anteprime accurate che includono elementi dinamici
Questa sezione illustra l'approccio consigliato per visualizzare più elementi in
l'anteprima di un widget per un widget con una visualizzazione
raccolta, ovvero un widget che utilizza
ListView
, GridView
o StackView
.
Se il widget utilizza una di queste viste, la creazione di un'anteprima scalabile fornendo direttamente il layout effettivo del widget peggiora l'esperienza quando l'anteprima del widget non mostra alcun elemento. Questo accade perché i dati delle visualizzazioni raccolta sono impostati in modo dinamico in fase di runtime e sono simili all'immagine mostrata nella figura 1.
Per fare in modo che le anteprime dei widget con viste raccolte vengano visualizzate correttamente nel selettore dei 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
vista raccolta segnaposto con elementi falsi. Ad esempio, puoi imitare un ListView
fornendo un segnaposto LinearLayout
con diversi elementi di elenco falsi.
Per illustrare un esempio per un elemento 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 di anteprima quando fornisci l'attributo previewLayout
dei
metadati AppWidgetProviderInfo
. Devi comunque specificare il layout effettivo del widget per l'attributo initialLayout
e usare il layout effettivo del widget durante la creazione di 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 elementi dell'elenco falsi, perché gli elementi
dell'elenco sono oggetti TextView
. Può essere
più complesso fornire elementi falsi se questi sono con layout complessi.
Considera un elemento dell'elenco definito in widget_list_item.xml
e composto 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 elementi di elenco falsi, puoi includere il layout più volte, ma in questo modo tutti gli elementi dell'elenco sono identici. Per fornire voci di elenco univoche, procedi nel seguente modo:
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 di 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 falsi 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>