Crea un adattatore di sincronizzazione

Nota: consigliamo WorkManager consigliata per la maggior parte dei casi d'uso relativi all'elaborazione in background. Fai riferimento alle guida all'elaborazione in background per scoprire quale soluzione funziona meglio per te.

Il componente dell'adattatore di sincronizzazione nella tua app contiene il codice per le attività che trasferiscono i dati tra il dispositivo e un server. In base alla pianificazione e agli attivatori che fornisci in nell'app, il framework dell'adattatore di sincronizzazione esegue il codice nel componente dell'adattatore di sincronizzazione. Per aggiungere un di sincronizzazione dell'adattatore alla tua app, devi aggiungere quanto segue:

Classe adattatore di sincronizzazione.
Una classe che aggrega il codice Data Transfer in un'interfaccia compatibile con l'adattatore di sincronizzazione. il modello di machine learning.
Associato Service.
Un componente che consente al framework dell'adattatore di sincronizzazione di eseguire il codice nell'adattatore di sincronizzazione .
File XML dei metadati dell'adattatore di sincronizzazione.
Un file contenente informazioni sull'adattatore di sincronizzazione. Il framework legge questo file scopri come caricare e pianificare il trasferimento dei dati.
Dichiarazioni nel file manifest dell'app.
XML che dichiara il servizio associato e punta a sincronizzare i metadati specifici dell'adattatore.

Questa lezione mostra come definire questi elementi.

Crea una classe dell'adattatore di sincronizzazione

In questa parte della lezione imparerai a creare la classe dell'adattatore di sincronizzazione che incapsula il codice di trasferimento dati. La creazione della classe include l'estensione della classe base dell'adattatore di sincronizzazione, la definizione costruttori per la classe e implementando il metodo in cui definisci il trasferimento attività di machine learning.

Estendi la classe dell'adattatore di sincronizzazione di base

Per creare il componente dell'adattatore di sincronizzazione, inizia estendendo AbstractThreadedSyncAdapter e scrivere i relativi costruttori. Utilizza la per eseguire attività di configurazione ogni volta che viene creato il componente dell'adattatore di sincronizzazione da zero, proprio come usi Activity.onCreate() per configurare attività. Ad esempio, se la tua app usa un fornitore di contenuti per archiviare i dati, utilizza i costruttori per ottenere un'istanza ContentResolver. Poiché una seconda forma è stato aggiunto nella piattaforma Android versione 3.0 per supportare parallelSyncs devi creare due forme del costruttore per mantenere la compatibilità.

Nota:la struttura dell'adattatore di sincronizzazione è progettata per funzionare con l'adattatore di sincronizzazione. che sono istanze singleton. La creazione dell'istanza del componente dell'adattatore di sincronizzazione è trattata in modo più dettagliato nella sezione Vincola l'adattatore di sincronizzazione al framework.

L'esempio seguente mostra come implementare AbstractThreadedSyncAdaptere i relativi costruttori:

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();
        ...
    }

Aggiungi il codice di trasferimento di dati

Il componente dell'adattatore di sincronizzazione non esegue automaticamente il trasferimento dei dati. Invece, incapsula il codice di trasferimento dei dati, in modo che il framework dell'adattatore di sincronizzazione possa eseguire trasferimento di dati in background, senza il coinvolgimento dell'app. Quando il framework è pronto per sincronizzare i dati dell'applicazione, richiama l'implementazione del metodo onPerformSync().

Per facilitare il trasferimento dei dati dal codice principale dell'app al componente dell'adattatore di sincronizzazione, il framework dell'adattatore di sincronizzazione onPerformSync() con i seguenti argomenti:

Account
Un oggetto Account associato all'evento che ha attivato l'elemento l'adattatore di sincronizzazione. Se il tuo server non utilizza account, non è necessario utilizzare informazioni in questo oggetto.
Extra
Un elemento Bundle contenente i flag inviati dall'evento che ha attivato la sincronizzazione dell'adattatore.
Competenza
L'autorità di un fornitore di contenuti nel sistema. L'app deve avere accesso a questo fornitore. In genere, l'autorità corrisponde a un fornitore di contenuti nella tua app.
di Gemini Advanced.
Client fornitore di contenuti
Il ContentProviderClient per il fornitore di contenuti indicato dal l'argomento dell'autorità di certificazione. Un ContentProviderClient è un pubblico leggero a un fornitore di contenuti. Ha le stesse funzionalità di base di un ContentResolver. Se utilizzi un fornitore di contenuti per archiviare i dati per la tua app, puoi connetterti al provider utilizzando questo oggetto. Altrimenti, puoi ignorare li annotino.
Risultato della sincronizzazione
Un oggetto SyncResult che utilizzi per inviare informazioni alla sincronizzazione framework dell'adattatore.

Lo snippet seguente mostra la struttura generale 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.
     */
}

Sebbene l'implementazione effettiva onPerformSync() è specifico per i requisiti per la sincronizzazione dei dati e i protocolli di connessione al server dell'app, esistono alcune attività generali che l'implementazione dovrebbe eseguire:

Connessione a un server
Anche se si può presumere che la rete sia disponibile all'inizio del trasferimento di dati, framework adattatori di sincronizzazione non si connette automaticamente a un server.
Download e caricamento dei dati
L'adattatore di sincronizzazione non automatizza le attività di trasferimento dei dati. Se vuoi scaricare dati da un server e li archivia in un fornitore di contenuti, devi fornire il codice richiede i dati, li scarica e li inserisce nel provider. Analogamente, se vuoi i dati a un server, devi leggerli da un file, un database o un provider e inviare la richiesta di caricamento necessaria. Devi anche gestire gli errori di rete che si verificano Data Transfer è in esecuzione.
Gestione dei conflitti di dati o determinazione del livello di attualità dei dati
Un adattatore di sincronizzazione non gestisce automaticamente i conflitti tra i dati sul server e i dati sul dispositivo. Inoltre, non rileva automaticamente se i dati sul server sono più recenti di i dati sul dispositivo o viceversa. ma devi fornire i tuoi algoritmi per gestire questa situazione.
Esegui la pulizia.
Chiudi sempre le connessioni a un server e pulisci i file temporanei e le cache alla fine il trasferimento dei dati.

Nota: il framework dell'adattatore di sincronizzazione viene eseguito onPerformSync() su thread in background, evitando così di dover configurare un'elaborazione in background personalizzata.

Oltre alle attività relative alla sincronizzazione, dovresti provare a combinare le tue normali relative alla rete e aggiungerle onPerformSync(). Concentrando tutte le attività di rete in questo metodo, risparmierai la carica della batteria necessarie per avviare e arrestare le interfacce di rete. Per scoprire di più su come migliorare l'accesso alla rete efficiente, consulta il corso Trasferimento di dati senza scaricare la batteria, in cui sono descritti diversi metodi di accesso che puoi includere nel codice di Data Transfer.

Associa l'adattatore di sincronizzazione al framework

Il codice di trasferimento dei dati è ora incapsulato in un componente dell'adattatore di sincronizzazione, ma per fornire al framework l'accesso al codice. Per farlo, devi creare un limite Service che trasmette un oggetto binder Android speciale dall'adattatore di sincronizzazione al framework. Con questo oggetto binder, il framework può richiamare il metodo onPerformSync() e passare dati al progetto.

Crea un'istanza del componente dell'adattatore di sincronizzazione come singleton nella onCreate() del servizio. Creando un'istanza il componente in onCreate(), rinviare creandolo fino all'avvio del servizio, cosa che accade quando il framework tenta per la prima volta di eseguire trasferimento di dati. Devi creare un'istanza del componente in modo sicuro per thread, nel caso in cui venga eseguita la sincronizzazione del framework dell'adattatore mette in coda più esecuzioni dell'adattatore di sincronizzazione in risposta ai trigger pianificazione.

Ad esempio, il seguente snippet mostra come creare una classe che implementa la funzione associato Service, crea un'istanza del componente adattatore di sincronizzazione e ottiene Oggetto binder 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:per vedere un esempio più dettagliato di un servizio associato per un adattatore di sincronizzazione, guarda l'app di esempio.

Aggiungi l'account richiesto dal framework

Il framework dell'adattatore di sincronizzazione richiede che ogni adattatore di sincronizzazione abbia un tipo di account. Hai dichiarato il valore del tipo di account nella sezione Aggiungi il file di metadati di Authenticator. Ora devi configurare questo tipo di account nel Sistema Android. Per configurare il tipo di account, aggiungi un account segnaposto che utilizzi il tipo di account chiamando il numero addAccountExplicitly().

Il modo migliore per chiamare il metodo è onCreate() metodo della tua app attività di apertura. Il seguente snippet di codice mostra come eseguire questa operazione:

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.
             */
        }
    }
    ...
}

Aggiungi il file dei metadati dell'adattatore di sincronizzazione

Per collegare il componente dell'adattatore di sincronizzazione al framework, devi fornire il framework con metadati che descrivono il componente e forniscono ulteriori flag. I metadati specificano il tipo di account che hai creato per l'adattatore di sincronizzazione, dichiara un'autorità del fornitore di contenuti associati alla tua app, controlla una parte dell'interfaccia utente di sistema relativa agli adattatori di sincronizzazione, e dichiara altri flag relativi alla sincronizzazione. Dichiara questi metadati in un file XML speciale archiviato in nella directory /res/xml/ nel progetto dell'app. Puoi assegnare qualsiasi nome al file, anche se di solito si chiama syncadapter.xml.

Questo file XML contiene un singolo elemento XML <sync-adapter> con i seguenti attributi:

android:contentAuthority
L'autorità URI del tuo fornitore di contenuti. Se è stato creato un fornitore di contenuti stub per nella lezione precedente Creare un fornitore di contenuti Stub, utilizza il valore specificato per attributo android:authorities nell'elemento <provider> che hai aggiunto al file manifest dell'app. Questo attributo è descritti più dettagliatamente nella sezione Dichiara il fornitore nel manifest.
Se trasferisci i dati da un fornitore di contenuti a un server con l'adattatore di sincronizzazione, deve corrispondere all'autorità per l'URI dei contenuti utilizzata per i dati. Questo valore è anche una delle autorità da te specificate nel android:authorities dell'elemento <provider> che dichiara il tuo fornitore nel file manifest dell'app.
android:accountType
Il tipo di account richiesto dal framework dell'adattatore di sincronizzazione. Il valore deve essere lo stesso come valore del tipo di account che hai fornito al momento della creazione del file dei metadati dell'autenticatore, descritto nella sezione Aggiungere il file di metadati di Authenticator. È anche il valore specificato per l'attributo costante ACCOUNT_TYPE nello snippet di codice nella sezione Aggiungere l'account richiesto dal framework.
Attributi delle impostazioni
android:userVisible
Consente di impostare la visibilità del tipo di account dell'adattatore di sincronizzazione. Per impostazione predefinita, l'icona e l'etichetta dell'account associate al tipo di account sono visibili nella Account dell'app Impostazioni del sistema, quindi dovresti effettuare la sincronizzazione adattatore invisibile a meno che tu non abbia un tipo di account o un dominio facilmente associabile con la tua app. Se rendi invisibile il tuo tipo di account, puoi comunque consentire agli utenti di controlla l'adattatore di sincronizzazione con un'interfaccia utente in una delle attività dell'app.
android:supportsUploading
Ti permette di caricare i dati sul cloud. Imposta questo elemento su false solo se la tua app scarica i dati.
android:allowParallelSyncs
Consente di eseguire contemporaneamente più istanze del componente dell'adattatore di sincronizzazione. Utilizza questa opzione se la tua app supporta più account utente e vuoi consentire agli utenti di trasferire i dati in parallelo. Questo flag non ha effetto se non esegui mai più trasferimenti di dati.
android:isAlwaysSyncable
Indica al framework dell'adattatore di sincronizzazione che può eseguire l'adattatore di sincronizzazione in qualsiasi all'ora specificata. Se vuoi controllare in modo programmatico quando la sincronizzazione l'adattatore può essere eseguito, imposta questo flag su false e richiama requestSync() per eseguire l'adattatore di sincronizzazione. Per ulteriori informazioni sull'esecuzione di un adattatore di sincronizzazione, consulta la Eseguire un adattatore di sincronizzazione

L'esempio seguente mostra l'XML per un adattatore di sincronizzazione che utilizza un singolo account segnaposto e esegue solo download.

<?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"/>

Dichiara l'adattatore di sincronizzazione nel file manifest

Dopo aver aggiunto il componente adattatore di sincronizzazione all'app, devi richiedere le autorizzazioni relative all'utilizzo del componente e devi dichiarare il vincolo Service che hai aggiunto.

Poiché il componente dell'adattatore di sincronizzazione esegue un codice che trasferisce i dati tra la rete e dispositivo, devi richiedere l'autorizzazione ad accedere a internet. Inoltre, la tua app deve per richiedere l'autorizzazione a leggere e scrivere le impostazioni dell'adattatore di sincronizzazione, così puoi controllare la sincronizzazione a livello di programmazione da altri componenti della tua app. Devi inoltre richiedere autorizzazione speciale che consente alla tua app di usare il componente di autenticazione che hai creato nella lezione Creazione di un'autenticatore Stub.

Per richiedere queste autorizzazioni, aggiungi quanto segue al file manifest dell'app come elementi secondari di <manifest>:

android.permission.INTERNET
Consente al codice dell'adattatore di sincronizzazione di accedere a Internet per scaricare o caricare dati dal dispositivo a un server. Non è necessario aggiungere di nuovo questa autorizzazione se in precedenza.
android.permission.READ_SYNC_SETTINGS
Consente all'app di leggere le impostazioni correnti dell'adattatore di sincronizzazione. Ad esempio, ti serve questo l'autorizzazione per chiamare getIsSyncable().
android.permission.WRITE_SYNC_SETTINGS
Consente all'app di controllare le impostazioni dell'adattatore di sincronizzazione. Devi disporre di questa autorizzazione per poter imposta le esecuzioni dell'adattatore di sincronizzazione periodica utilizzando addPeriodicSync(). Questa autorizzazione non è necessaria per chiamare requestSync(). Per scoprire di più su che utilizza l'adattatore di sincronizzazione, consulta Esecuzione di un adattatore di sincronizzazione.

Lo snippet seguente mostra come aggiungere le autorizzazioni:

<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>

Infine, per dichiarare il valore Service associato che il framework utilizza per interagisci con l'adattatore di sincronizzazione, aggiungi il seguente XML al file manifest dell'app come elemento secondario di <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>

La <intent-filter> configura un filtro che viene attivato dall'azione di intent android.content.SyncAdapter, inviato dal sistema per eseguire l'adattatore di sincronizzazione. Quando il filtro , il sistema avvia il servizio associato che hai creato, che in questo esempio SyncService. L'attributo android:exported="false" consente solo alla tua app e al sistema di accedere Service. L'attributo android:process=":sync" indica al sistema di eseguire Service in un processo condiviso globale denominato sync. Se nella tua app sono presenti più adattatori di sincronizzazione, gli utenti possono condividere questo processo, con conseguente riduzione dei costi generali.

La <meta-data> fornisce il nome del file XML dei metadati dell'adattatore di sincronizzazione che hai creato in precedenza. La android:name indica che questi metadati riguardano il framework dell'adattatore di sincronizzazione. La android:resource specifica il nome del file di metadati.

Ora hai tutti i componenti dell'adattatore di sincronizzazione. La prossima lezione ti mostrerà come indica al framework dell'adattatore di sincronizzazione di eseguire l'adattatore di sincronizzazione, in risposta a un evento o il giorno con una programmazione regolare.