Observação:recomendamos o WorkManager como a solução recomendada para a maioria dos casos de uso de processamento em segundo plano. Consulte o guia de processamento em segundo plano para saber qual solução funciona melhor para você.
O componente adaptador de sincronização no app encapsula o código das tarefas que são transferidas dados entre o dispositivo e um servidor. Com base na programação e nos acionadores fornecidos no seu app, o framework do adaptador de sincronização executará o código no componente do adaptador de sincronização. Para adicionar um do adaptador de sincronização ao seu app, será preciso adicionar as seguintes partes:
- Classe do adaptador de sincronização.
- Classe que encapsula seu código de transferência de dados em uma interface compatível com o adaptador de sincronização de análise de dados em nuvem.
-
Service
vinculado. - Componente que permite que o framework do adaptador de sincronização execute o código no adaptador de sincronização .
- Arquivo de metadados XML do adaptador de sincronização.
- Um arquivo contendo informações sobre o adaptador de sincronização. O framework lê esse arquivo para saiba como carregar e programar sua transferência de dados.
- Declarações no manifesto do app.
- XML que declara o serviço vinculado e aponta para metadados específicos do adaptador de sincronização.
Esta lição mostra como definir esses elementos.
Criar uma classe de adaptador de sincronização
Nesta parte da lição, você aprenderá a criar a classe do adaptador de sincronização que encapsula o código de transferência de dados. A criação da classe inclui estender a classe base do adaptador de sincronização, definindo construtores para a classe e implementar o método em que você define a transferência de dados tarefas.
Estender a classe base do adaptador de sincronização
Para criar o componente do adaptador de sincronização, comece estendendo
AbstractThreadedSyncAdapter
e criar os construtores dele. Use o
construtores para executar tarefas de configuração sempre que o componente adaptador de sincronização é criado
do zero, assim como você usa Activity.onCreate()
para configurar um
atividades. Por exemplo, se o app usa um provedor de conteúdo para armazenar dados, use os construtores
para receber uma instância de ContentResolver
. Como uma segunda forma do
O construtor foi adicionado na plataforma Android versão 3.0 para oferecer suporte ao parallelSyncs
.
você precisa criar duas formas do construtor para manter a compatibilidade.
Observação:o framework do adaptador de sincronização foi projetado para funcionar com o adaptador de sincronização. componentes que são instâncias singleton. A criação de instâncias do componente adaptador de sincronização é abordada com mais detalhes na seção Vincule o adaptador de sincronização ao framework.
O exemplo abaixo mostra como implementar
AbstractThreadedSyncAdapter
e os construtores dela:
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(); ... }
Adicionar o código de transferência de dados
O componente do adaptador de sincronização não faz a transferência de dados automaticamente. Em vez disso,
encapsula seu código de transferência de dados, para que o framework do adaptador de sincronização possa executar a
transferência de dados em segundo plano, sem o envolvimento do seu app. Quando o framework estiver pronto
para sincronizar os dados do seu aplicativo, ele invoca a implementação do método
onPerformSync()
:
Para facilitar a transferência de dados do código do app principal para o componente do adaptador de sincronização,
o framework do adaptador de sincronização chama
onPerformSync()
com o
seguintes argumentos:
- Conta
-
Um objeto
Account
associado ao evento que acionou. o adaptador de sincronização. Caso seu servidor não use contas, não é necessário usar o método informações neste objeto. - Extras
-
Um
Bundle
contendo flags enviadas pelo evento que acionou a sincronização. por um adaptador. - Autoridade
- A autoridade de um provedor de conteúdo no sistema. Seu app precisa ter acesso a esse provedor. Normalmente, a autoridade corresponde a um provedor de conteúdo no seu próprio app. .
- Cliente do provedor de conteúdo
-
Um
ContentProviderClient
do provedor de conteúdo indicado pelo de autoridade. UmContentProviderClient
é um arquivo público para um provedor de conteúdo. Ele tem as mesmas funcionalidades básicas de umContentResolver
: Se você estiver usando um provedor de conteúdo para armazenar dados para seu app, é possível se conectar ao provedor com este objeto. Caso contrário, ignore reimplantá-lo. - Resultado da sincronização
-
Um objeto
SyncResult
usado para enviar informações para a sincronização do adaptador de rede.
O snippet a seguir mostra a estrutura geral
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. */ }
Embora a implementação real
onPerformSync()
é específico para
requisitos de sincronização de dados do app e protocolos de conexão de servidor,
tarefas gerais que sua implementação deve realizar:
- Conectar-se a um servidor
- Embora você possa presumir que a rede estará disponível quando sua transferência de dados for iniciada, a o framework do adaptador de sincronização não se conecta automaticamente a um servidor.
- Fazer o download e upload de dados
- Os adaptadores de sincronização não automatizam nenhuma tarefa de transferência de dados. Se você quiser fazer o download dados de um servidor e armazená-los em um provedor de conteúdo, é necessário fornecer o código solicita os dados, faz o download deles e os insere no provedor. Da mesma forma, se você quiser enviar dados a um servidor, você precisa lê-los de um arquivo, banco de dados ou provedor e a solicitação de upload necessária. Você também precisa lidar com erros de rede que ocorrem transferência de dados está em execução.
- Gerenciar conflitos de dados ou determinar a atualidade dos dados
- Um adaptador de sincronização não lida automaticamente com conflitos entre dados no servidor e dados no dispositivo. Além disso, ele não detecta automaticamente se os dados no servidor são mais recentes do que os dados no dispositivo ou vice-versa. Em vez disso, você precisa fornecer algoritmos próprios para ao lidar com essa situação.
- Limpeza
- Sempre feche as conexões com um servidor e limpe os arquivos temporários e os caches ao final do sua transferência de dados.
Observação:o framework do adaptador de sincronização é executado
onPerformSync()
em uma
linha de execução em segundo plano, para que você não precise configurar seu próprio processamento em segundo plano.
Além de suas tarefas relacionadas à sincronização, você deve tentar combinar suas tarefas regulares
tarefas relacionadas à rede e adicioná-las
onPerformSync()
:
Ao concentrar todas as tarefas de rede nesse método, você economiza a energia da bateria
necessárias para iniciar e interromper as interfaces de rede. Para saber mais sobre como tornar o acesso à rede
eficiente, consulte o curso de treinamento Transferência de dados sem consumo de bateria, que descreve diversas informações sobre o acesso à rede
tarefas que podem ser incluídas
no código de transferência de dados.
Vincular o adaptador de sincronização ao framework
Agora você tem seu código de transferência de dados encapsulado em um componente do adaptador de sincronização, mas
para dar ao framework acesso ao código. Para fazer isso, você precisa criar um vinculado
Service
, que transmite um objeto de vinculação especial Android do adaptador de sincronização.
ao framework. Com esse objeto binder, o framework pode invocar o
onPerformSync()
e
transmitir dados para ele.
Instanciar o componente adaptador de sincronização como um singleton no
onCreate()
do serviço. Ao instanciar
o componente em onCreate()
, você adia
criando-o até que o serviço seja iniciado, o que acontece quando o framework tenta executar pela primeira vez
transferência de dados. Você precisa instanciar o componente de maneira segura para a linha de execução, caso a sincronização
o framework do adaptador coloca várias execuções em fila em resposta a gatilhos ou
programação.
Por exemplo, o snippet a seguir mostra como criar uma classe que implementa o
vinculado ao Service
, instancia o componente do adaptador de sincronização e recebe a
Objeto de vinculação do 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(); } }
Observação: para ver um exemplo mais detalhado de um serviço vinculado a um adaptador de sincronização, consulte o app de exemplo.
Adicionar a conta exigida pelo framework
O framework do adaptador de sincronização exige que cada adaptador de sincronização tenha um tipo de conta. Você declarou
o valor do tipo de conta na seção
Adicione o arquivo de metadados do Authenticator. Agora você precisa configurar esse tipo de conta no
sistema Android. Para configurar o tipo de conta, adicione uma conta de marcador que use o tipo de conta
chame addAccountExplicitly()
.
O melhor lugar para chamar o método é no
onCreate()
do método
atividade de abertura. O snippet de código a seguir mostra como fazer isso.
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. */ } } ... }
Adicionar o arquivo de metadados do adaptador de sincronização
Para conectar o componente do adaptador de sincronização ao framework, é necessário fornecer o framework
com metadados que descrevem o componente e fornecem sinalizações adicionais. Os metadados especificam
o tipo de conta que você criou para o adaptador de sincronização, declara uma autoridade do provedor de conteúdo
associada ao seu aplicativo, controla uma parte da interface de usuário do sistema relacionada a adaptadores de sincronização,
e declara outras flags relacionadas à sincronização. Declare esses metadados em um arquivo XML especial armazenado em
no diretório /res/xml/
no projeto do app. Você pode dar qualquer nome ao arquivo,
embora seja geralmente chamado de syncadapter.xml
.
Esse arquivo XML contém um único elemento XML <sync-adapter>
que tem o
seguintes atributos:
android:contentAuthority
-
A autoridade de URI para seu provedor de conteúdo. Se você criou um provedor de conteúdo stub para
seu app na lição anterior, Como criar um provedor de conteúdo stub, use o valor especificado para o
atributo
android:authorities
no elemento<provider>
adicionado ao manifesto do app. Esse atributo é descritos em mais detalhes na seção Declarar o provedor no manifesto.
Se você transferir dados de um provedor de conteúdo para um servidor com o adaptador de sincronização, deve ser o mesmo da autoridade de URI de conteúdo usada para esses dados. Esse valor também é uma das autoridades especificadas naandroid:authorities
atributo do elemento<provider>
que declara o provedor no manifesto do app. android:accountType
-
O tipo de conta exigido pelo framework do adaptador de sincronização. O valor precisa ser o mesmo
como o valor do tipo de conta fornecido quando você criou o arquivo de metadados do autenticador, conforme
descritos na seção Adicionar o arquivo de metadados do autenticador. É também o valor especificado para o valor
constante
ACCOUNT_TYPE
no snippet de código da seção Adicione a conta exigida pela Estrutura. - Atributos de configuração
-
-
android:userVisible
- Define a visibilidade do tipo de conta do adaptador de sincronização. Por padrão, o o ícone e o rótulo associados ao tipo de conta ficam visíveis no Contas do app Configurações do sistema. Por isso, faça a sincronização. adaptador invisível, a menos que você tenha um tipo de conta ou domínio que seja facilmente associado com seu app. Se você tornar seu tipo de conta invisível, ainda poderá permitir que os usuários controlar o adaptador de sincronização com uma interface do usuário em uma das atividades do app.
-
android:supportsUploading
-
Permite fazer upload de dados para a nuvem. Defina como
false
se apenas seu app de downloads. -
android:allowParallelSyncs
- Permite que várias instâncias do adaptador de sincronização sejam executadas ao mesmo tempo. Use esta opção se o app for compatível com várias contas de usuário e você quiser permitir várias transferir dados em paralelo. Essa sinalização não terá efeito se você nunca executar várias transferências de dados.
-
android:isAlwaysSyncable
-
Indica ao framework do adaptador de sincronização que ele pode ser executado em qualquer
o horário especificado. Se você quiser controlar programaticamente quando a sincronização
adaptador pode ser executado, defina essa flag como
false
e chamerequestSync()
para executar adaptador de sincronização. Para saber mais sobre como executar um adaptador de sincronização, consulte a lição Executar um adaptador de sincronização
-
O exemplo a seguir mostra o XML de um adaptador de sincronização que usa uma única conta de marcador de posição e só faz downloads.
<?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"/>
Declarar o adaptador de sincronização no manifesto
Depois de adicionar o componente do adaptador de sincronização ao app, é necessário solicitar permissões
relacionadas ao uso do componente, e é necessário declarar a propriedade Service
vinculada
que você adicionou.
Como o componente adaptador de sincronização executa um código que transfere dados entre a rede e a dispositivo, será necessário solicitar permissão para acessar a Internet. Além disso, seu app precisa para solicitar permissão de leitura e gravação das configurações do adaptador de sincronização, para controlar a sincronização de forma programática com outros componentes do app. Você também precisa solicitar uma permissão especial que autoriza o app a usar o componente autenticador que você criou na lição Como criar um stub de autenticação.
Para solicitar essas permissões, adicione o seguinte ao manifesto do app como elementos filhos de
<manifest>
:
-
android.permission.INTERNET
- Permite que o código do adaptador de sincronização acesse a Internet para fazer o download ou upload de dados do dispositivo para um servidor. Não é necessário adicionar essa permissão novamente caso você tenha sido antes de tê-lo solicitado.
-
android.permission.READ_SYNC_SETTINGS
-
Permite que o app leia as configurações atuais do adaptador de sincronização. Por exemplo, você precisa que
permissão para chamar
getIsSyncable()
. -
android.permission.WRITE_SYNC_SETTINGS
-
Permite que o app controle as configurações do adaptador de sincronização. Você precisa dessa permissão para
o adaptador de sincronização periódico definido é executado usando
addPeriodicSync()
. Essa permissão não é necessária para chamarrequestSync()
Para saber mais sobre use o adaptador de sincronização em Executar um adaptador de sincronização.
O snippet a seguir mostra como adicionar as permissões.
<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 fim, para declarar o Service
vinculado que o framework usa para
interagir com o adaptador de sincronização, adicione o seguinte XML ao manifesto do app como um elemento filho
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>
A
<intent-filter>
define um filtro que é acionado pela ação da intent
android.content.SyncAdapter
, enviada pelo sistema para executar o adaptador de sincronização. Quando o filtro
for acionado, o sistema iniciará o serviço vinculado que você criou, que neste exemplo é
SyncService
: O atributo
android:exported="false"
permite que apenas seu app e o sistema acessem o
Service
. O atributo
android:process=":sync"
instrui o sistema a executar a Service
em um processo global compartilhado chamado
sync
Se você tiver vários adaptadores de sincronização no app, eles podem compartilhar esse processo,
o que reduz a sobrecarga.
A
<meta-data>
fornece o nome do arquivo XML de metadados do adaptador de sincronização criado anteriormente.
A
android:name
indica que os metadados são para o framework do adaptador de sincronização. A
android:resource
especifica o nome do arquivo de metadados.
Agora você tem todos os componentes para seu adaptador de sincronização. A próxima aula mostra como instruirá o framework do adaptador de sincronização a executá-lo, seja em resposta a um evento ou em dentro de um cronograma regular.