Cómo crear un widget avanzado

Prueba hacerlo con Compose
Jetpack Compose es el kit de herramientas de IU recomendado para Android. Aprende a compilar widgets con las APIs de estilo Compose.

En esta página, se explican las prácticas recomendadas para crear un widget más avanzado y brindar una mejor experiencia del usuario.

Optimizaciones para actualizar el contenido del widget

Actualizar el contenido del widget puede ser costoso en términos de procesamiento. Para ahorrar consumo de batería, optimiza el tipo, la frecuencia y el tiempo de actualización.

Tipos de actualizaciones de widgets

Existen 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 costos de procesamiento y ramificaciones.

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 por completo el widget. Esto reemplaza el proporcionado anteriormente RemoteViews por un nuevo RemoteViews. 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: Llama a AppWidgetManager.partiallyUpdateAppWidget para actualizar partes del widget. Esto combina el RemoteViews nuevo con el RemoteViews proporcionado anteriormente. Se ignora este método si un widget no recibe al menos una actualización completa a través de 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);
  • Actualización de datos de la colección: llama AppWidgetManager.notifyAppWidgetViewDataChanged para invalidar los datos de una vista de colección en tu widget. Esto activa RemoteViewsFactory.onDataSetChanged. Mientras tanto, los datos antiguos se muestran en el widget. Puedes realizar tareas costosas de forma segura y 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 lugar de tu app, siempre que tenga el mismo UID que la clase correspondiente AppWidgetProvider.

Determina con qué frecuencia actualizar un widget

Los widgets se actualizan periódicamente según el valor proporcionado para el updatePeriodMillis atributo. El widget puede actualizarse en respuesta a la interacción del usuario, las actualizaciones de transmisión o ambas.

Actualizar periódicamente

Puedes controlar la frecuencia de la actualización periódica especificando un valor para AppWidgetProviderInfo.updatePeriodMillis en el XML appwidget-provider. Cada actualización activa el método AppWidgetProvider.onUpdate(), que es donde puedes colocar el código para actualizar el widget. Sin embargo, considera las alternativas para las actualizaciones del receptor de transmisiones que se describen en la siguiente sección si tu widget necesita cargar datos de forma asíncrona o tarda más de 10 segundos en actualizarse, ya que, después de 10 segundos, el sistema considera que un BroadcastReceiver no responde.

updatePeriodMillis no admite valores de menos de 30 minutos. Sin embargo, si deseas inhabilitar las actualizaciones periódicas, puedes especificar 0.

Puedes permitir que los usuarios ajusten la frecuencia de las actualizaciones en una configuración. Por ejemplo, es posible que quieran que un código bursátil se actualice cada 15 minutos o solo cuatro veces al día. En este caso, configura 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 a AppWidgetManager.updateAppWidget en respuesta a una interacción del usuario, como una presión del usuario.

  • Desde interacciones remotas, como una notificación o un widget de app: Construye un PendingIntent y, luego, actualiza el widget desde el Activity, Broadcast o Service invocado. Puedes elegir tu propia prioridad. Por ejemplo, si seleccionas un Broadcast para el PendingIntent, puedes elegir una transmisión en primer plano para darle BroadcastReceiver prioridad.

Actualizar en respuesta a un evento de transmisión

Un ejemplo de un evento de transmisión que requiere que se actualice un widget es cuando el usuario toma una foto. En este caso, deseas actualizar el widget cuando se detecta una foto nueva.

Puedes programar un trabajo con JobScheduler y especificar una transmisión como el activador con el JobInfo.Builder.addTriggerContentUri método.

También puedes registrar un BroadcastReceiver para la transmisión, por ejemplo, escuchar ACTION_LOCALE_CHANGED. Sin embargo, como esto consume recursos del dispositivo, úsalo con cuidado y escucha solo la transmisión específica. Con la introducción de limitaciones de transmisión en Android 7.0 (nivel de API 24) y Android 8.0 (nivel de API 26), las apps no pueden registrar transmisiones implícitas en sus manifiestos, con ciertas excepciones.

Consideraciones para actualizar un widget desde un BroadcastReceiver

Si el widget se actualiza desde un BroadcastReceiver, incluido AppWidgetProvider, ten en cuenta las siguientes consideraciones sobre la duración y la prioridad de una actualización del widget.

Duración de la actualización

Como regla general, el sistema permite que los receptores de emisión, que suelen ejecutarse en el subproceso principal de la app, se ejecuten hasta por 10 segundos antes de considerarlos que no responden y activar un error de Application Not Responding (ANR). Para evitar bloquear el subproceso principal mientras se controla la transmisión, usa el goAsync método. Si tarda más en actualizar el widget, considera programar una tarea con WorkManager.

Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.

Consulta Consideraciones y prácticas recomendadas de seguridad para obtener más información.

Prioridad de la actualización

De forma predeterminada, las transmisiones, incluidas las que se realizan con AppWidgetProvider.onUpdate, se ejecutan como procesos en segundo plano. Esto significa que los recursos del sistema sobrecargados pueden causar una demora en la invocación del receptor de emisión. Para priorizar la transmisión, conviértela en un proceso en primer plano.

Por ejemplo, agrega la Intent.FLAG_RECEIVER_FOREGROUND marca a la Intent que se pasa a la PendingIntent.getBroadcast cuando el usuario presiona una parte determinada del widget.