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.
El componente del adaptador de sincronización de tu app encapsula el código para las tareas que se transfieren datos entre el dispositivo y un servidor. Según la programación y los activadores que proporciones en tu app, el framework del adaptador de sincronización ejecuta el código en el componente del adaptador de sincronización. Para agregar un de adaptador de sincronización a tu app, debes agregar lo siguiente:
- Clase de adaptador de sincronización
- Una clase que une tu código de transferencia de datos en una interfaz compatible con el adaptador de sincronización. en un framework de aplicaciones.
- 
        Se vincula Service.
- Un componente que permite que el framework del adaptador de sincronización ejecute el código en tu adaptador de sincronización .
- Archivo de metadatos XML del adaptador de sincronización
- Un archivo que contiene información sobre tu adaptador de sincronización. El framework lee este archivo descubre cómo cargar y programar tu transferencia de datos.
- Declaraciones en el manifiesto de la app
- Un archivo XML en el que se declara el servicio vinculado y apunta para sincronizar metadatos específicos del adaptador.
En esta lección, se muestra cómo definir estos elementos.
Cómo crear una clase de adaptador de sincronización
En esta parte de la lección, aprenderás a crear la clase de adaptador de sincronización que encapsula la código de transferencia de datos. Crear la clase incluye extender la clase base del adaptador de sincronización, definir constructores para la clase y la implementación del método en el que defines la transferencia de datos tareas.
Cómo extender la clase de adaptador de sincronización de base
    Para crear el componente del adaptador de sincronización, extiende
    AbstractThreadedSyncAdapter y escribe sus constructores. Usa el
    de configuración para ejecutar tareas de configuración cada vez que se cree el componente del adaptador de sincronización.
    desde cero, tal como usas Activity.onCreate() para configurar una
    actividad. Por ejemplo, si tu app usa un proveedor de contenido para almacenar datos, usa los constructores
    para obtener una instancia de ContentResolver. Dado que la segunda versión del
    se agregó en la versión 3.0 de la plataforma de Android para admitir parallelSyncs
    debes crear dos formas del constructor para mantener la compatibilidad.
Nota: El framework del adaptador de sincronización está diseñado para funcionar con este tipo de adaptador. que son instancias singleton. Se aborda la creación de instancias del componente del adaptador de sincronización con más detalle en la sección Vincula el adaptador de sincronización al framework.
    En el siguiente ejemplo, se muestra cómo implementar
    AbstractThreadedSyncAdaptery sus constructores:
Kotlin
/** * Handle the transfer of data between a server and an * app, using the Android sync adapter framework. */ class SyncAdapter @JvmOverloads constructor( context: Context, autoInitialize: Boolean, /** * Using a default argument along with @JvmOverloads * generates constructor for both method signatures to maintain compatibility * with Android 3.0 and later platform versions */ allowParallelSyncs: Boolean = false, /* * If your app uses a content resolver, get an instance of it * from the incoming Context */ val mContentResolver: ContentResolver = context.contentResolver ) : AbstractThreadedSyncAdapter(context, autoInitialize, allowParallelSyncs) { ... }
Java
/** * Handle the transfer of data between a server and an * app, using the Android sync adapter framework. */ public class SyncAdapter extends AbstractThreadedSyncAdapter { ... // Global variables // Define a variable to contain a content resolver instance ContentResolver contentResolver; /** * Set up the sync adapter */ public SyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); /* * If your app uses a content resolver, get an instance of it * from the incoming Context */ contentResolver = context.getContentResolver(); } ... /** * Set up the sync adapter. This form of the * constructor maintains compatibility with Android 3.0 * and later platform versions */ public SyncAdapter( Context context, boolean autoInitialize, boolean allowParallelSyncs) { super(context, autoInitialize, allowParallelSyncs); /* * If your app uses a content resolver, get an instance of it * from the incoming Context */ contentResolver = context.getContentResolver(); ... }
Cómo agregar el código de transferencia de datos
    El componente del adaptador de sincronización no hace la transferencia de datos automáticamente. Por el contrario,
    encapsula tu código de transferencia de datos, de modo que el framework del adaptador de sincronización pueda ejecutar la
    transferencia de datos en segundo plano, sin la participación de tu app. Cuando el framework esté listo
    para sincronizar los datos de tu aplicación, esta invoca tu implementación del método
    onPerformSync()
    Para facilitar la transferencia de datos desde el código principal de la app al componente del adaptador de sincronización,
    el framework del adaptador de sincronización llama
    onPerformSync() con el
    los siguientes argumentos:
- Cuenta
- 
        Un objeto Accountasociado con el evento que activó el adaptador de sincronización. Si el servidor no usa cuentas, no es necesario que uses el información de este objeto.
- Adicionales
- 
        Un Bundleque contiene marcas enviadas por el evento que activó la sincronización adaptador.
- Autoridad
- Es la autoridad de un proveedor de contenido en el sistema. Tu app debe tener acceso a este proveedor. Por lo general, la autoridad corresponde a un proveedor de contenido de tu propia app.
- Cliente proveedor de contenido
- 
        Un ContentProviderClientpara el proveedor de contenido al que apunta el el argumento de autoridad. UnContentProviderClientes un objeto público ligero a un proveedor de contenido. Tiene la misma funcionalidad básica que unContentResolverSi usas un proveedor de contenido para almacenar datos para tu app, puedes conectarte al proveedor que tenga este objeto. De lo contrario, puedes ignorar que la modifica.
- Resultado de sincronización
- 
        Un objeto SyncResultque usas para enviar información a la sincronización de Adaptador de software.
    El siguiente fragmento muestra la estructura general de
    onPerformSync():
Kotlin
/* * Specify the code you want to run in the sync adapter. The entire * sync adapter runs in a background thread, so you don't have to set * up your own background processing. */ override fun onPerformSync( account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult ) { /* * Put the data transfer code here. */ }
Java
/* * Specify the code you want to run in the sync adapter. The entire * sync adapter runs in a background thread, so you don't have to set * up your own background processing. */ @Override public void onPerformSync( Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { /* * Put the data transfer code here. */ }
    Si bien la implementación real de
    onPerformSync() es específico de
    los requisitos de sincronización de datos y los protocolos de conexión del servidor, hay algunos
    tareas generales que debe realizar tu implementación:
- Conexión con un servidor
- Si bien puedes suponer que la red está disponible cuando comienza la transferencia de datos, el del adaptador de sincronización no se conecta automáticamente a un servidor.
- Descarga y carga de datos
- Un adaptador de sincronización no automatiza ninguna tarea de transferencia de datos. Si quieres descargar datos de un servidor y almacenarlos en un proveedor de contenido, debes proporcionar el código que solicita los datos, los descarga y los inserta en el proveedor. Del mismo modo, si quieres enviar datos a un servidor, debes leerlos desde un archivo, una base de datos o un proveedor y la solicitud de carga necesaria. También debes solucionar los errores de red que se produzcan se está ejecutando la transferencia de datos.
- Control de los conflictos de datos o determinación de la actualidad los datos
- Un adaptador de sincronización no controla automáticamente los conflictos entre los datos del servidor y los datos en el dispositivo. Además, no detecta automáticamente si los datos en el servidor son más recientes que los datos en el dispositivo, o viceversa. En cambio, debes proporcionar tus propios algoritmos para para manejar esta situación.
- Limpieza
- Cerrar siempre las conexiones a un servidor y limpiar los archivos temporales y los cachés al final de la transferencia de datos.
    Nota: El framework del adaptador de sincronización se ejecuta
    onPerformSync() en un
    subproceso en segundo plano para que no debas configurar
tu propio procesamiento en segundo plano.
    Además de las tareas relacionadas con la sincronización, debe tratar de combinar sus
    tareas relacionadas con la red y agregarlas
    onPerformSync()
    Si concentras todas las tareas de red en este método, conservas la energía de la batería
    necesarias para iniciar y detener las interfaces de red. Si quieres obtener más información para hacer que el acceso a la red sea más
    eficiente, consulta la clase de capacitación Cómo transferir datos sin consumir la batería, en la que se describen varios accesos a la red
    tareas que puedes incluir en tu código de transferencia de datos.
Cómo vincular el adaptador de sincronización con el marco de trabajo
    Ahora tienes tu código de transferencia de datos encapsulado en un componente de adaptador de sincronización, pero
    para proporcionarle al framework acceso a tu código. Para ello, necesitas crear un límite
    Service que pasa un objeto vinculante especial de Android desde el adaptador de sincronización.
    en el framework. Con este objeto vinculante, el framework puede invocar al
    Método onPerformSync() y
    pasarle datos.
    Crea una instancia de tu componente de adaptador de sincronización como un singleton en la
    onCreate() del servicio. Creando una instancia
    el componente en onCreate(), diferirás
    lo creas hasta que se inicia el servicio, lo que sucede cuando el framework intenta ejecutar tu
    la transferencia de datos. Debes crear una instancia del componente
de manera segura para subprocesos, en caso de que la
    del adaptador pone en cola múltiples ejecuciones de tu adaptador de sincronización en respuesta a activadores o
    y la programación.
    Por ejemplo, en el siguiente fragmento, se muestra cómo crear una clase que implementa la
    vinculado Service, crea una instancia del componente del adaptador de sincronización y obtiene el
    Objeto vinculante de Android:
Kotlin
package com.example.android.syncadapter /** * Define a Service that returns an [android.os.IBinder] for the * sync adapter class, allowing the sync adapter framework to call * onPerformSync(). */ class SyncService : Service() { /* * Instantiate the sync adapter object. */ override fun onCreate() { /* * Create the sync adapter as a singleton. * Set the sync adapter as syncable * Disallow parallel syncs */ synchronized(sSyncAdapterLock) { sSyncAdapter = sSyncAdapter ?: SyncAdapter(applicationContext, true) } } /** * Return an object that allows the system to invoke * the sync adapter. * */ override fun onBind(intent: Intent): IBinder { /* * Get the object that allows external processes * to call onPerformSync(). The object is created * in the base class code when the SyncAdapter * constructors call super() * * We should never be in a position where this is called before * onCreate() so the exception should never be thrown */ return sSyncAdapter?.syncAdapterBinder ?: throw IllegalStateException() } companion object { // Storage for an instance of the sync adapter private var sSyncAdapter: SyncAdapter? = null // Object to use as a thread-safe lock private val sSyncAdapterLock = Any() } }
Java
package com.example.android.syncadapter; /** * Define a Service that returns an <code><a href="/reference/android/os/IBinder.html">IBinder</a></code> for the * sync adapter class, allowing the sync adapter framework to call * onPerformSync(). */ public class SyncService extends Service { // Storage for an instance of the sync adapter private static SyncAdapter sSyncAdapter = null; // Object to use as a thread-safe lock private static final Object sSyncAdapterLock = new Object(); /* * Instantiate the sync adapter object. */ @Override public void onCreate() { /* * Create the sync adapter as a singleton. * Set the sync adapter as syncable * Disallow parallel syncs */ synchronized (sSyncAdapterLock) { if (sSyncAdapter == null) { sSyncAdapter = new SyncAdapter(getApplicationContext(), true); } } } /** * Return an object that allows the system to invoke * the sync adapter. * */ @Override public IBinder onBind(Intent intent) { /* * Get the object that allows external processes * to call onPerformSync(). The object is created * in the base class code when the SyncAdapter * constructors call super() */ return sSyncAdapter.getSyncAdapterBinder(); } }
Nota: Para ver un ejemplo más detallado de un servicio vinculado para un adaptador de sincronización, consulta lo siguiente: consulta la app de ejemplo.
Cómo agregar la cuenta obligatoria para el framework
    El framework del adaptador de sincronización requiere que cada adaptador de sincronización tenga un tipo de cuenta. Declaraste
    el valor del tipo de cuenta en la sección
    Agrega el archivo de metadatos del Autenticador. Ahora debes configurar este tipo de cuenta en la
    Sistema Android Para configurar el tipo de cuenta, agrega una cuenta de marcador de posición que use el tipo de cuenta
    llamando a addAccountExplicitly().
    El mejor lugar para llamar al método es en la
    método onCreate() de la API de
    la actividad de apertura. En el siguiente fragmento de código, se muestra cómo hacerlo:
Kotlin
... // Constants // The authority for the sync adapter's content provider const val AUTHORITY = "com.example.android.datasync.provider" // An account type, in the form of a domain name const val ACCOUNT_TYPE = "example.com" // The account name const val ACCOUNT = "placeholderaccount" ... class MainActivity : FragmentActivity() { // Instance fields private lateinit var mAccount: Account ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Create the placeholder account mAccount = createSyncAccount() ... } ... /** * Create a new placeholder account for the sync adapter */ private fun createSyncAccount(): Account { val accountManager = getSystemService(Context.ACCOUNT_SERVICE) as AccountManager return Account(ACCOUNT, ACCOUNT_TYPE).also { newAccount -> /* * Add the account and account type, no password or user data * If successful, return the Account object, otherwise report an error. */ if (accountManager.addAccountExplicitly(newAccount, null, null)) { /* * If you don't set android:syncable="true" in * in your <provider> element in the manifest, * then call context.setIsSyncable(account, AUTHORITY, 1) * here. */ } else { /* * The account exists or some other error occurred. Log this, report it, * or handle it internally. */ } } } ... }
Java
public class MainActivity extends FragmentActivity { ... ... // Constants // The authority for the sync adapter's content provider public static final String AUTHORITY = "com.example.android.datasync.provider"; // An account type, in the form of a domain name public static final String ACCOUNT_TYPE = "example.com"; // The account name public static final String ACCOUNT = "placeholderaccount"; // Instance fields Account mAccount; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Create the placeholder account mAccount = CreateSyncAccount(this); ... } ... /** * Create a new placeholder account for the sync adapter * * @param context The application context */ public static Account CreateSyncAccount(Context context) { // Create the account type and default account Account newAccount = new Account( ACCOUNT, ACCOUNT_TYPE); // Get an instance of the Android account manager AccountManager accountManager = (AccountManager) context.getSystemService( ACCOUNT_SERVICE); /* * Add the account and account type, no password or user data * If successful, return the Account object, otherwise report an error. */ if (accountManager.addAccountExplicitly(newAccount, null, null)) { /* * If you don't set android:syncable="true" in * in your <provider> element in the manifest, * then call context.setIsSyncable(account, AUTHORITY, 1) * here. */ } else { /* * The account exists or some other error occurred. Log this, report it, * or handle it internally. */ } } ... }
Cómo agregar el archivo de metadatos del adaptador de sincronización
    Para conectar el componente del adaptador de sincronización con el framework, debes proporcionar el framework
    con metadatos que describen el componente y proporcionan marcas adicionales. Los metadatos especifican
    el tipo de cuenta que creaste para el adaptador de sincronización, declara la autoridad del proveedor de contenido
    asociada con tu app, controla una parte de la interfaz de usuario del sistema relacionada con los adaptadores de sincronización
    y declara otras marcas relacionadas con la sincronización. Declara estos metadatos en un archivo XML especial almacenado en
    el directorio /res/xml/ en el proyecto de tu app. Puedes darle cualquier nombre al archivo,
    aunque generalmente se llama syncadapter.xml.
    Este archivo en formato XML contiene un único elemento XML <sync-adapter> que tiene el elemento
    siguientes atributos:
- android:contentAuthority
- 
        Es la autoridad del URI para tu proveedor de contenido. Si creaste un proveedor de contenido de stub para
        tu app en la lección anterior Cómo crear un proveedor de contenido auxiliar, usa el valor que especificaste para el
        atributo
android:authoritiesen el elemento<provider>que agregaste al manifiesto de tu app. Este atributo es se describe con más detalle en la sección Cómo declarar el proveedor en el manifiesto.
 Si estás transfiriendo datos de un proveedor de contenido a un servidor con tu adaptador de sincronización, este debe ser la misma que la autoridad del URI de contenido que usas para esos datos. Este valor también es una de las autoridades que especificas en elandroid:authoritiesdel elemento<provider>que declara tu proveedor en el manifiesto de la app.
- android:accountType
- 
        El tipo de cuenta requerido por el framework del adaptador de sincronización. El valor debe ser el mismo
        como el valor del tipo de cuenta que proporcionaste cuando creaste el archivo de metadatos del autenticador,
        que se describe en la sección Cómo agregar el archivo de metadatos del Autenticador. También es el valor que especificaste para el atributo
        la constante ACCOUNT_TYPEen el fragmento de código de la sección Agrega la cuenta requerida por el framework.
- Atributos de configuración
- 
        - 
                android:userVisible
- Establece la visibilidad del tipo de cuenta del adaptador de sincronización. De forma predeterminada, el el ícono y la etiqueta asociados al tipo de cuenta están visibles en la Cuentas de la aplicación Configuración del sistema, por lo que debes realizar la sincronización. el adaptador sea invisible a menos que tengas un tipo de cuenta o dominio que se asocie fácilmente con tu app. Aunque establezcas el tipo de cuenta como invisible, puedes permitir que los usuarios controlar tu adaptador de sincronización con una interfaz de usuario en una de las actividades de tu app.
- 
                android:supportsUploading
- 
                Te permite subir datos a la nube. Establece este parámetro en falsesi solo tu app descarga datos.
- 
                android:allowParallelSyncs
- Permite que se ejecuten al mismo tiempo varias instancias del componente de tu adaptador de sincronización. Úsalo si tu app admite varias cuentas de usuario y quieres permitir varias para que los usuarios transfieran datos en paralelo. Esta marca no tiene efecto si nunca ejecutas múltiples transferencias de datos.
- 
                android:isAlwaysSyncable
- 
                Indica al framework del adaptador de sincronización que puede ejecutar el adaptador de sincronización en cualquier
                tiempo que has especificado. Si quieres controlar de forma programática cuándo se sincroniza
                puede ejecutarse, establecer esta marca en falsey, luego, llamarrequestSync()para ejecutar adaptador de sincronización. Para obtener más información sobre cómo ejecutar un adaptador de sincronización, consulta la lección Cómo ejecutar un adaptador de sincronización
 
- 
                
En el siguiente ejemplo, se muestra el XML de un adaptador de sincronización que usa una cuenta de marcador de posición única y solo realiza descargas.
<?xml version="1.0" encoding="utf-8"?> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="com.example.android.datasync.provider" android:accountType="com.android.example.datasync" android:userVisible="false" android:supportsUploading="false" android:allowParallelSyncs="false" android:isAlwaysSyncable="true"/>
Cómo declarar el adaptador de sincronización en el manifiesto
    Una vez que hayas agregado el componente del adaptador de sincronización a tu app, deberás solicitar permisos
    relacionadas con el uso del componente, y debes declarar el Service vinculado.
    que agregaste.
Dado que el componente del adaptador de sincronización ejecuta un código que transfiere datos entre la red y dispositivo, debes solicitar permiso para acceder a Internet. Además, tu app necesita solicitar permiso para leer y escribir la configuración del adaptador de sincronización, de modo que puedas controlar la sincronización de forma programática desde otros componentes de tu app. También debes solicitar permiso especial que permite que tu app use el componente autenticador que creaste Consulta la lección Cómo crear un autenticador auxiliar.
    Para solicitar estos permisos, agrega lo siguiente al manifiesto de la app como elementos secundarios de
<manifest>:
- 
        android.permission.INTERNET
- Permite que el código del adaptador de sincronización acceda a Internet para poder descargar o cargar datos del dispositivo a un servidor. No es necesario que vuelvas a agregar este permiso si lo solicitaron previamente.
- 
android.permission.READ_SYNC_SETTINGS
- 
        Permite que tu app lea la configuración actual del adaptador de sincronización. Por ejemplo, necesitas esta
        permiso para llamar a getIsSyncable().
- 
android.permission.WRITE_SYNC_SETTINGS
- 
        Permite que tu app controle la configuración del adaptador de sincronización. Necesitas este permiso para
        establece ejecuciones periódicas del adaptador de sincronización con addPeriodicSync()Este permiso no es necesario para llamarrequestSync()Para obtener más información ejecutar el adaptador de sincronización, consulta Cómo ejecutar un adaptador de sincronización.
En el siguiente fragmento, se muestra cómo agregar los permisos:
<manifest> ... <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> ... </manifest>
    Por último, declara el Service vinculado que el framework usa para
    interactuar con tu adaptador de sincronización, agrega el siguiente XML al manifiesto de tu app como un elemento secundario
    de <application>:
<service android:name="com.example.android.datasync.SyncService" android:exported="false" android:process=":sync"> <intent-filter> <action android:name="android.content.SyncAdapter"/> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" /> </service>
    El
<intent-filter>
    elemento configura un filtro que se activa con la acción de intent
    android.content.SyncAdapter, que envía el sistema para ejecutar el adaptador de sincronización. Cuando el filtro
    , el sistema inicia el servicio vinculado que creaste, que en este ejemplo es
    SyncService El atributo
android:exported="false"
    permite que solo tu aplicación y el sistema accedan a la
    Service El atributo
android:process=":sync"
    le indica al sistema que ejecute Service en un proceso compartido global llamado
    sync Si tienes varios adaptadores de sincronización en tu app, estos pueden compartir este proceso.
    lo que reduce la sobrecarga.
    El
<meta-data>
    proporciona el nombre del archivo XML de metadatos del adaptador de sincronización que creaste anteriormente.
    El
android:name
    indica que estos metadatos son para el framework del adaptador de sincronización. El
android:resource
    especifica el nombre del archivo de metadatos.
Ahora tienes todos los componentes para el adaptador de sincronización. En la próxima lección, se muestra indicar al framework del adaptador de sincronización que ejecute el adaptador, ya sea en respuesta a un evento o un cronograma regular.
