En esta página, se explican las prácticas recomendadas para crear un widget más avanzado para una mejor experiencia del usuario.
Optimizaciones para actualizar el contenido de los widgets
Actualizar el contenido del widget puede ser costoso en términos de procesamiento. Para ahorrar batería y el consumo, optimizar el tipo de actualización, la frecuencia y los tiempos.
Tipos de actualizaciones de widgets
Hay tres formas de actualizar un widget: una actualización completa, una actualización parcial y En el caso de un widget de colección, una actualización de datos. Cada uno tiene diferentes los costos y las ramificaciones de la computación.
A continuación, se describe cada tipo de actualización y se proporcionan fragmentos de código para cada uno.
Actualización completa: Llama a
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
para actualizar el widget por completo. Esto reemplaza al proporcionadoRemoteViews
con un nuevoRemoteViews
Esta es la actualización más costosa en términos de procesamiento.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);
Actualización parcial: Llamada
AppWidgetManager.partiallyUpdateAppWidget
para actualizar partes del widget. De esta manera, se combina el nuevoRemoteViews
con elRemoteViews
proporcionado anteriormente. Este método se ignora si un widget no reciba al menos una actualización completa a través deupdateAppWidget(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);
Actualización de datos de la recopilación: Llamada
AppWidgetManager.notifyAppWidgetViewDataChanged
para invalidar los datos de una vista de colección en tu widget. Esto activaRemoteViewsFactory.onDataSetChanged
Mientras tanto, los datos antiguos se muestran en el widget. Puedes realizar tareas costosas de forma síncrona con este método.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);
Puedes llamar a estos métodos desde cualquier parte de tu app, siempre y cuando la app tenga el elemento
con el mismo UID que el
AppWidgetProvider
.
Cómo determinar la frecuencia con la que quieres actualizar un widget
Los widgets se actualizan periódicamente según el valor proporcionado para el
updatePeriodMillis
. El widget se puede actualizar en respuesta a la interacción del usuario, transmitir
actualizaciones o ambas.
Actualizar periódicamente
Puedes controlar la frecuencia de la actualización periódica especificando un valor para
AppWidgetProviderInfo.updatePeriodMillis
en el XML de appwidget-provider
. Cada
update activa el método AppWidgetProvider.onUpdate()
, que es donde
puedes colocar el código para actualizar el widget. Sin embargo, considera las alternativas para
actualizaciones del receptor de emisión que se describen en un
sección si tu widget necesita cargar datos de forma asíncrona o si tarda más
de 10 segundos para actualizarse, porque después de 10 segundos, el sistema considera
BroadcastReceiver
para no responder
updatePeriodMillis
no admite valores menores a 30 minutos. Sin embargo, si
si quieres inhabilitar las actualizaciones periódicas, puedes especificar 0.
Puedes permitir que los usuarios ajusten la frecuencia de las actualizaciones en una configuración. Para
ejemplo, es posible que quieran que un visor de acciones se actualice cada 15 minutos o solo a cuatro
varias veces al día. En este caso, establece el updatePeriodMillis
en 0 y usa
WorkManager
en su lugar.
Actualizar en respuesta a una interacción del usuario
Estas son algunas formas recomendadas de actualizar el widget según la interacción del usuario:
Desde una actividad de la app: Llama directamente.
AppWidgetManager.updateAppWidget
en respuesta a una interacción del usuario, como a medida que el usuario lo presiona.Desde interacciones remotas, como una notificación o el widget de una app: crea un
PendingIntent
y, luego, actualiza el widget a partir del elemento invocadoActivity
,Broadcast
oService
. Puedes elegir tu propia prioridad. Para Por ejemplo, si seleccionas unBroadcast
para elPendingIntent
, puedes elegir una transmisión en primer plano para que la Prioridad deBroadcastReceiver
.
Actualizar en respuesta a un evento de transmisión
Un ejemplo de un evento de emisión que requiere que se actualice un widget es cuando el el usuario toma una foto. En este caso, quieres actualizar el widget cuando se muestre una foto nueva si detecta posibles problemas.
Puedes programar un trabajo con JobScheduler
y especificar una transmisión como el
activador con el
JobInfo.Builder.addTriggerContentUri
.
También puedes registrar un BroadcastReceiver
para la transmisión, por ejemplo,
escuchar
ACTION_LOCALE_CHANGED
Sin embargo, debido a que esto consume recursos del dispositivo, utilízalo con cuidado y escucha
solo a una transmisión específica. Con la introducción de la transmisión
limitaciones en Android
7.0 (nivel de API 24) y Android 8.0 (nivel de API 26), las apps no pueden registrar valores implícitos
transmisiones en sus manifiestos, con ciertas
excepciones.
Consideraciones para actualizar un widget desde un BroadcastReceiver
Si el widget se actualiza desde un BroadcastReceiver
, lo que incluye
AppWidgetProvider
, ten en cuenta las siguientes consideraciones sobre el
la duración y la prioridad de la actualización de un widget.
Duración de la actualización
Como regla general, el sistema permite receptores de emisión, que generalmente se ejecutan en la subproceso principal, ejecútalo por hasta 10 segundos antes de considerar que no responden y activando un error Aplicación no Error de respuesta (ANR). Si lleva más tiempo actualizar el widget, considera las siguientes alternativas:
Programar una tarea con
WorkManager
Dale más tiempo al receptor
goAsync
. Esto permite que los receptores se ejecuten durante 30 segundos.
Consulta las Consideraciones de seguridad y recomendaciones prácticas para obtener más información información.
Prioridad de la actualización
De forma predeterminada, las transmisiones, incluidas las realizadas con
AppWidgetProvider.onUpdate
: Se ejecutan como procesos en segundo plano. Esto significa
La sobrecarga de recursos del sistema puede provocar un retraso en la invocación de la transmisión
receptor. Para priorizar la transmisión, haz que sea un proceso en primer plano.
Por ejemplo, agrega
Intent.FLAG_RECEIVER_FOREGROUND
marca a la Intent
que se pasa al PendingIntent.getBroadcast
cuando el usuario
toca en una parte determinada del widget.
Crea vistas previas precisas que incluyan elementos dinámicos.
En esta sección, se explica el enfoque recomendado para mostrar varios elementos en
una vista previa del widget con una colección
View, es decir, un widget que usa una
ListView
, GridView
o StackView
.
Si tu widget utiliza una de estas vistas, crea una vista previa escalable directamente proporcionando el widget diseño degrada la cuando la vista previa del widget no muestra elementos. Esto ocurre porque los datos de la vista de colección se configuran dinámicamente en el tiempo de ejecución y son similares al que se muestra en la figura 1.
Para que las vistas previas de los widgets con vistas de colección se muestren correctamente en el widget
selector, recomendamos mantener un archivo de diseño separado designado solo para la
vista previa. Este archivo de diseño separado incluye el diseño real del widget y una
vista de colección de marcador de posición con elementos falsos. Por ejemplo, puedes imitar un
ListView
proporcionando un marcador de posición LinearLayout
con varias listas falsas
elementos.
Para ilustrar un ejemplo de un ListView
, comienza con un archivo de diseño independiente:
// 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>
Especifica el archivo de diseño de vista previa cuando proporciones el atributo previewLayout
de
los metadatos AppWidgetProviderInfo
Aún debes especificar el diseño real del widget
para el atributo initialLayout
y usa el diseño real del widget cuando
construyendo un RemoteViews
en el tiempo de ejecución
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
Elementos de lista complejos
El ejemplo de la sección anterior proporciona elementos de lista falsos, porque la lista
los elementos son objetos TextView
. Sí, es posible
para proporcionar elementos falsos si estos son diseños complejos.
Considera un elemento de lista que se define en widget_list_item.xml
y consta de lo siguiente:
dos objetos 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>
Para proporcionar elementos de lista falsos, puedes incluir el diseño varias veces, pero esta hace que cada elemento de la lista sea idéntico. Para proporcionar elementos de lista únicos, sigue estos pasos:
Crea un conjunto de atributos para los valores de texto:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
Usa estos atributos para configurar el texto:
<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 todos los estilos que sea necesario para la vista previa. Vuelve a definir los valores en cada estilo:
<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>
Aplica los estilos en los elementos falsos en el diseño de vista previa:
<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>