Nota: Recomendamos usar WorkManager. como la solución recomendada para la mayoría de los casos de uso de procesamiento en segundo plano. Consulta el guía de procesamiento en segundo plano para descubrir qué solución te conviene más.
En las lecciones anteriores de esta clase, aprendiste a crear un componente de adaptador de sincronización que encapsula código de transferencia de datos y cómo agregar componentes adicionales enchufa el adaptador de sincronización al sistema. Ahora tienes todo lo que necesitas para instalar una app que incluye un adaptador de sincronización, pero ningún código que viste ejecuta realmente el adaptador.
Debes intentar ejecutar tu adaptador de sincronización según un programa o como resultado indirecto de alguna para cada evento. Por ejemplo, tal vez quieras que el adaptador de sincronización se ejecute de forma periódica, ya sea después de de un período determinado o a una hora particular del día. También puedes ejecutar la sincronización cuando se realizan cambios en los datos almacenados en el dispositivo. Debes evitar ejecutar tu de sincronización como el resultado directo de una acción del usuario, ya que al hacer esto, y la ventaja de la capacidad de programación del framework del adaptador de sincronización. Por ejemplo, debes evitar proporcionando un botón de actualización en tu interfaz de usuario.
Tienes las siguientes opciones para ejecutar tu adaptador de sincronización:
- Cuando cambian los datos del servidor
- Ejecuta el adaptador de sincronización en respuesta a un mensaje de un servidor que indique que el servidor los datos cambiaron. Esta opción te permite actualizar los datos del servidor al dispositivo. sin deteriorar el rendimiento ni desperdiciar la duración de la batería con un sondeo del servidor.
- Cuando cambian los datos del dispositivo
- Ejecuta un adaptador de sincronización cuando cambien los datos en el dispositivo. Esta opción te permite enviar datos modificados del dispositivo a un servidor, y es muy útil si necesitas asegurarte de que el servidor siempre tenga los datos más recientes del dispositivo. Esta opción es fácil de implementar si almacenas datos en tu proveedor de contenido. Si usas un stub el proveedor de contenido, la detección de los cambios en los datos puede ser más difícil.
- A intervalos regulares
- Ejecuta un adaptador de sincronización después de que caduque un intervalo que elijas, o ejecútalo en un tiempo todos los días.
- On demand
- Ejecuta el adaptador de sincronización en respuesta a una acción del usuario. Sin embargo, para proporcionar la mejor experiencia del usuario, debe confiar principalmente en una de las opciones más automatizadas. Mediante opciones automatizadas, ahorras batería y recursos de red.
En el resto de esta lección, se describe con más detalles cada una de las opciones.
Cómo ejecutar el adaptador de sincronización cuando cambian los datos del servidor
Si tu app transfiere datos de un servidor y estos cambian con frecuencia, puedes usar
que usa un adaptador de sincronización para hacer descargas
en respuesta a cambios en los datos. Para ejecutar el adaptador de sincronización, haz lo siguiente:
el servidor envía un mensaje especial a un BroadcastReceiver
en tu app.
En respuesta a este mensaje, llama a ContentResolver.requestSync()
para indicarle al framework del adaptador de sincronización que ejecute tu
adaptador de sincronización.
El envío de mensajes a través de la nube de Google Cloud (GCM) proporciona la
del servidor y del dispositivo que necesites
para que funcione este sistema de mensajería. Usa GCM para activar
de datos es más confiable y eficiente que sondear los servidores para conocer el estado. Durante el sondeo
requiere un Service
que esté siempre activo, GCM utiliza un
BroadcastReceiver
que se activa cuando llega un mensaje. Durante el sondeo
a intervalos regulares usa la energía de la batería incluso si no hay actualizaciones disponibles, GCM solo envía
mensajes cuando sea necesario.
Nota: Si utilizas GCM para activar tu adaptador de sincronización a través de una transmisión a todos en los que esté instalada tu aplicación, recuerda que reciben tu mensaje en más o menos al mismo tiempo. Esta situación puede hacer que se ejecuten varias instancias del adaptador de sincronización al mismo tiempo, lo que provoca sobrecargas del servidor y la red. Para evitar esta situación durante una transmisión para todos los dispositivos, debes considerar postergar un tiempo el inicio del adaptador de sincronización que sea única para cada dispositivo.
En el siguiente fragmento de código, se muestra cómo ejecutar
requestSync()
en respuesta a un
mensaje entrante de GCM:
Kotlin
... // Constants // Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Account type const val ACCOUNT_TYPE = "com.example.android.datasync" // Account const val ACCOUNT = "default_account" // Incoming Intent key for extended data const val KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST" ... class GcmBroadcastReceiver : BroadcastReceiver() { ... override fun onReceive(context: Context, intent: Intent) { // Get a GCM object instance val gcm: GoogleCloudMessaging = GoogleCloudMessaging.getInstance(context) // Get the type of GCM message val messageType: String? = gcm.getMessageType(intent) /* * Test the message type and examine the message contents. * Since GCM is a general-purpose messaging system, you * may receive normal messages that don't require a sync * adapter run. * The following code tests for a a boolean flag indicating * that the message is requesting a transfer from the device. */ if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE == messageType && intent.getBooleanExtra(KEY_SYNC_REQUEST, false)) { /* * Signal the framework to run your sync adapter. Assume that * app initialization has already created the account. */ ContentResolver.requestSync(mAccount, AUTHORITY, null) ... } ... } ... }
Java
public class GcmBroadcastReceiver extends BroadcastReceiver { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account type public static final String ACCOUNT_TYPE = "com.example.android.datasync"; // Account public static final String ACCOUNT = "default_account"; // Incoming Intent key for extended data public static final String KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST"; ... @Override public void onReceive(Context context, Intent intent) { // Get a GCM object instance GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); // Get the type of GCM message String messageType = gcm.getMessageType(intent); /* * Test the message type and examine the message contents. * Since GCM is a general-purpose messaging system, you * may receive normal messages that don't require a sync * adapter run. * The following code tests for a a boolean flag indicating * that the message is requesting a transfer from the device. */ if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType) && intent.getBooleanExtra(KEY_SYNC_REQUEST)) { /* * Signal the framework to run your sync adapter. Assume that * app initialization has already created the account. */ ContentResolver.requestSync(mAccount, AUTHORITY, null); ... } ... } ... }
Cómo ejecutar el adaptador de sincronización cuando cambian los datos del proveedor de contenido
Si tu app recopila datos en un proveedor de contenido y quieres actualizar el servidor cada vez
cuando actualizas el proveedor, puedes configurar tu app para que ejecute el adaptador de sincronización automáticamente. Tareas pendientes
debes registrar un observador para el proveedor de contenido. Cuando se almacenan datos en tu proveedor de contenido
cambios, el framework del proveedor de contenido llama al observador. En el observador, llama
requestSync()
para indicarle al framework que se ejecute.
tu adaptador de sincronización.
Nota: Si usas un proveedor de contenido de stub, no tienes datos en
el proveedor de contenido y onChange()
es
que nunca se llamó. En este caso, tiene que proporcionar su propio mecanismo
para detectar cambios en
del dispositivo. Este mecanismo también se encarga de llamar
requestSync()
cuando cambien los datos
Para crear un observador para tu proveedor de contenido, extiende la clase.
ContentObserver
e implementará ambas formas de su
onChange()
. En
onChange()
, llamada
requestSync()
para iniciar el adaptador de sincronización.
Para registrar el observador, pásalo como argumento en una llamada a
registerContentObserver()
En
esta llamada, también debes pasar un URI de contenido para los datos que quieras observar. El contenido
del proveedor compara este URI de visualización con los URI de contenido que se pasaron como argumentos para
ContentResolver
que modifiquen tu proveedor, como
ContentResolver.insert()
Si hay una coincidencia,
implementación de ContentObserver.onChange()
se llama.
En el siguiente fragmento de código, se muestra cómo definir un ContentObserver
que llama a requestSync()
cuando una tabla
cambios:
Kotlin
// Constants // Content provider scheme const val SCHEME = "content://" // Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Path for the content provider table const val TABLE_PATH = "data_table" ... class MainActivity : FragmentActivity() { ... // A content URI for the content provider's data table private lateinit var uri: Uri // A content resolver for accessing the provider private lateinit var mResolver: ContentResolver ... inner class TableObserver(...) : ContentObserver(...) { /* * Define a method that's called when data in the * observed content provider changes. * This method signature is provided for compatibility with * older platforms. */ override fun onChange(selfChange: Boolean) { /* * Invoke the method signature available as of * Android platform version 4.1, with a null URI. */ onChange(selfChange, null) } /* * Define a method that's called when data in the * observed content provider changes. */ override fun onChange(selfChange: Boolean, changeUri: Uri?) { /* * Ask the framework to run your sync adapter. * To maintain backward compatibility, assume that * changeUri is null. */ ContentResolver.requestSync(account, AUTHORITY, null) } ... } ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Get the content resolver object for your app mResolver = contentResolver // Construct a URI that points to the content provider data table uri = Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .path(TABLE_PATH) .build() /* * Create a content observer object. * Its code does not mutate the provider, so set * selfChange to "false" */ val observer = TableObserver(false) /* * Register the observer for the data table. The table's path * and any of its subpaths trigger the observer. */ mResolver.registerContentObserver(uri, true, observer) ... } ... }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider scheme public static final String SCHEME = "content://"; // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Path for the content provider table public static final String TABLE_PATH = "data_table"; // Account public static final String ACCOUNT = "default_account"; // Global variables // A content URI for the content provider's data table Uri uri; // A content resolver for accessing the provider ContentResolver mResolver; ... public class TableObserver extends ContentObserver { /* * Define a method that's called when data in the * observed content provider changes. * This method signature is provided for compatibility with * older platforms. */ @Override public void onChange(boolean selfChange) { /* * Invoke the method signature available as of * Android platform version 4.1, with a null URI. */ onChange(selfChange, null); } /* * Define a method that's called when data in the * observed content provider changes. */ @Override public void onChange(boolean selfChange, Uri changeUri) { /* * Ask the framework to run your sync adapter. * To maintain backward compatibility, assume that * changeUri is null. */ ContentResolver.requestSync(mAccount, AUTHORITY, null); } ... } ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Get the content resolver object for your app mResolver = getContentResolver(); // Construct a URI that points to the content provider data table uri = new Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .path(TABLE_PATH) .build(); /* * Create a content observer object. * Its code does not mutate the provider, so set * selfChange to "false" */ TableObserver observer = new TableObserver(false); /* * Register the observer for the data table. The table's path * and any of its subpaths trigger the observer. */ mResolver.registerContentObserver(uri, true, observer); ... } ... }
Cómo ejecutar el adaptador de sincronización de manera periódica
Puedes ejecutar tu adaptador de sincronización periódicamente configurando un período de espera entre ejecuciones o al ejecutarlo en ciertos momentos del día, o ambos. Cómo ejecutar el adaptador de sincronización Periódicamente, te permite coincidir aproximadamente con el intervalo de actualización de tu servidor.
Del mismo modo, puedes subir datos desde el dispositivo cuando tu servidor está relativamente inactivo, programar tu adaptador de sincronización para que se ejecute por la noche. La mayoría de los usuarios dejan sus dispositivos encendidos y conectados. por la noche, por lo que suele estar disponible. Además, el dispositivo no ejecuta otras tareas en al mismo tiempo que tu adaptador de sincronización. Sin embargo, si adoptas este enfoque, debes asegurarte de que cada dispositivo activa una transferencia de datos en un momento ligeramente diferente. Si todos los dispositivos ejecutan de sincronización al mismo tiempo, es probable que se sobrecarguen los datos del servidor y del proveedor de telefonía celular redes.
En general, las ejecuciones periódicas tienen sentido si los usuarios no necesitan actualizaciones instantáneas, pero esperan actualizaciones periódicas. Las ejecuciones periódicas también son útiles si quieres equilibrar la disponibilidad de datos actualizados con la eficiencia de ejecuciones más pequeñas del adaptador de sincronización que no usan el dispositivo en exceso de Google Cloud.
Para ejecutar tu adaptador de sincronización a intervalos regulares, llama
addPeriodicSync()
De esta forma, se programa tu
que el adaptador de sincronización se ejecute después de que haya transcurrido un período determinado. Dado que el framework del adaptador de sincronización
tiene que tener en cuenta otras ejecuciones del adaptador e intenta maximizar la eficiencia de la batería,
el tiempo transcurrido puede variar unos segundos. Además, el framework no ejecutará tu adaptador de sincronización si la
la red no está disponible.
Observa que addPeriodicSync()
no
y ejecutar el adaptador de sincronización
a una hora determinada del día. Para ejecutar el adaptador de sincronización en la
todos los días a la misma hora, usa una alarma repetitiva como disparador. Las alarmas repetitivas se describen en más
en la documentación de referencia de AlarmManager
. Si usas
método setInexactRepeating()
para establecer
de hora del día que tienen alguna variación, aún debes aleatorizar la hora de inicio para
asegurarse de que el adaptador de sincronización se ejecute desde diferentes dispositivos estén escalonados.
El método addPeriodicSync()
no
inhabilitar setSyncAutomatically()
,
por lo que puede tener varias ejecuciones
de sincronización en un período relativamente corto. Además, solo algunos
las marcas de control del adaptador de sincronización en una llamada a
addPeriodicSync()
; las marcas que se
no permitidos se describen en la documentación de referencia para
addPeriodicSync()
En el siguiente fragmento de código, se muestra cómo programar ejecuciones periódicas del adaptador de sincronización:
Kotlin
// Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Account const val ACCOUNT = "default_account" // Sync interval constants const val SECONDS_PER_MINUTE = 60L const val SYNC_INTERVAL_IN_MINUTES = 60L const val SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE ... class MainActivity : FragmentActivity() { ... // A content resolver for accessing the provider private lateinit var mResolver: ContentResolver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Get the content resolver for your app mResolver = contentResolver /* * Turn on periodic syncing */ ContentResolver.addPeriodicSync( mAccount, AUTHORITY, Bundle.EMPTY, SYNC_INTERVAL) ... } ... }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account public static final String ACCOUNT = "default_account"; // Sync interval constants public static final long SECONDS_PER_MINUTE = 60L; public static final long SYNC_INTERVAL_IN_MINUTES = 60L; public static final long SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE; // Global variables // A content resolver for accessing the provider ContentResolver mResolver; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Get the content resolver for your app mResolver = getContentResolver(); /* * Turn on periodic syncing */ ContentResolver.addPeriodicSync( mAccount, AUTHORITY, Bundle.EMPTY, SYNC_INTERVAL); ... } ... }
Cómo ejecutar el adaptador de sincronización a pedido
Ejecutar el adaptador de sincronización en respuesta a una solicitud de usuario es la estrategia menos preferible. para ejecutar un adaptador de sincronización. El framework se diseñó específicamente para conservar batería. cuando ejecuta adaptadores de sincronización según un programa. Opciones que ejecutan una sincronización en respuesta a los datos los cambios usan la energía de la batería de manera eficaz, ya que la energía se usa para proporcionar datos nuevos.
En cambio, permitir que los usuarios ejecuten una sincronización a pedido significa que la sincronización se ejecuta sola, lo que es el uso ineficiente de los recursos de red y energía. Además, ofrecer sincronización a pedido lleva a los usuarios a solicitar una sincronización incluso si no hay evidencia de que los datos hayan cambiado, y ejecutar una sincronización que no actualiza datos es un uso ineficaz de la energía de la batería. En general, tu app debería Usar otros indicadores para activar una sincronización o programarlas a intervalos regulares sin intervención del usuario
Sin embargo, si deseas ejecutar el adaptador de sincronización a pedido, configura las marcas del adaptador de sincronización para un
ejecuta el adaptador de sincronización manual y, luego, llama
ContentResolver.requestSync()
Ejecuta transferencias a pedido con las siguientes marcas:
-
SYNC_EXTRAS_MANUAL
-
Fuerza una sincronización manual. El framework del adaptador de sincronización ignora la configuración existente.
como la marca establecida por
setSyncAutomatically()
. -
SYNC_EXTRAS_EXPEDITED
- Hace que la sincronización comience de inmediato. Si no lo configuras, es posible que el sistema espere varias segundos antes de ejecutar la solicitud de sincronización, ya que intenta optimizar el uso de la batería programar muchas solicitudes en un período corto.
En el siguiente fragmento de código, se muestra cómo llamar
requestSync()
en respuesta a un botón
clic:
Kotlin
// Constants // Content provider authority val AUTHORITY = "com.example.android.datasync.provider" // Account type val ACCOUNT_TYPE = "com.example.android.datasync" // Account val ACCOUNT = "default_account" ... class MainActivity : FragmentActivity() { ... // Instance fields private lateinit var mAccount: Account ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... /* * Create the placeholder account. The code for CreateSyncAccount * is listed in the lesson Creating a Sync Adapter */ mAccount = createSyncAccount() ... } /** * Respond to a button click by calling requestSync(). This is an * asynchronous operation. * * This method is attached to the refresh button in the layout * XML file * * @param v The View associated with the method call, * in this case a Button */ fun onRefreshButtonClick(v: View) { // Pass the settings flags by inserting them in a bundle val settingsBundle = Bundle().apply { putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) } /* * Request the sync for the default account, authority, and * manual sync settings */ ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle) }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account type public static final String ACCOUNT_TYPE = "com.example.android.datasync"; // Account public static final String ACCOUNT = "default_account"; // Instance fields Account mAccount; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... /* * Create the placeholder account. The code for CreateSyncAccount * is listed in the lesson Creating a Sync Adapter */ mAccount = CreateSyncAccount(this); ... } /** * Respond to a button click by calling requestSync(). This is an * asynchronous operation. * * This method is attached to the refresh button in the layout * XML file * * @param v The View associated with the method call, * in this case a Button */ public void onRefreshButtonClick(View v) { // Pass the settings flags by inserting them in a bundle Bundle settingsBundle = new Bundle(); settingsBundle.putBoolean( ContentResolver.SYNC_EXTRAS_MANUAL, true); settingsBundle.putBoolean( ContentResolver.SYNC_EXTRAS_EXPEDITED, true); /* * Request the sync for the default account, authority, and * manual sync settings */ ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle); }