Estou tentando implementar o padrão Content-Provider-Sync Adapter, conforme discutido no Google IO - slide 26. Meu provedor de conteúdo está funcionando e minha sincronização funciona quando eu a aciono no aplicativo Dev Tools Sync Tester, no entanto, quando chamo ContentResolver. requestSync (conta, autoridade, pacote) de meu ContentProvider, minha sincronização nunca é acionada.
ContentResolver.requestSync(
account,
AUTHORITY,
new Bundle());
Editar - snippet de manifesto adicionado Meu manifesto xml contém:
<service
android:name=".sync.SyncService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
--Editar
Meu syncadapter.xml associado ao meu serviço de sincronização contém:
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="AUTHORITY"
android:accountType="myaccounttype"
android:supportsUploading="true"
/>
Não tenho certeza de que outro código seria útil. A conta passada para requestSync é "myaccounttype" e a AUTHORITY passada para a chamada corresponde ao meu xml do adaptador syc.
ContentResolver.requestSync é a maneira correta de solicitar uma sincronização? Parece que a ferramenta do testador de sincronização se liga diretamente ao serviço e chama o início da sincronização, mas parece que isso anula o propósito de integração com a arquitetura de sincronização.
Se essa for a maneira correta de solicitar uma sincronização, por que o testador de sincronização funcionaria, mas não minha chamada para ContentResolver.requestSync? Há algo que preciso passar no pacote?
Estou testando no emulador em dispositivos rodando 2.1 e 2.2.
android:process=":sync"
do serviço de sincronização permite que o depurador acerte os pontos críticos. O serviço de sincronização em si estava funcionando antes disso, porque eu podia ver as mensagens de log doonPerformSync
método em nome de outro processo.Respostas:
A chamada
requestSync()
só funcionará em um par {Account, ContentAuthority} conhecido pelo sistema. Seu aplicativo precisa passar por uma série de etapas para informar ao Android que você é capaz de sincronizar um tipo específico de conteúdo usando um tipo específico de conta. Ele faz isso no AndroidManifest.1. Notifique o Android de que o pacote do seu aplicativo fornece sincronização
Em primeiro lugar, no AndroidManifest.xml, você deve declarar que possui um serviço de sincronização:
O atributo name da
<service>
tag é o nome da sua classe para conectar a sincronização ... Falarei sobre isso em um segundo.Definir exportado como verdadeiro o torna visível para outros componentes (necessário para
ContentResolver
poder chamá-lo).O filtro de intent permite capturar uma sincronização de solicitação de intent. (Isso
Intent
vemContentResolver
quando você ligaContentResolver.requestSync()
ou métodos de agendamento relacionados).A
<meta-data>
tag será discutida abaixo.2. Fornece ao Android um serviço usado para encontrar seu SyncAdapter
Portanto, a própria classe ... Aqui está um exemplo:
Sua classe deve estender
Service
ou uma de suas subclasses, deve implementarpublic IBinder onBind(Intent)
e deve retornar umSyncAdapterBinder
quando for chamado ... Você precisa de uma variável do tipoAbstractThreadedSyncAdapter
. Como você pode ver, isso é praticamente tudo nessa classe. A única razão pela qual está lá é para fornecer um serviço, que oferece uma interface padrão para Android para consultar sua classe sobre o que vocêSyncAdapter
é.3. Forneça um
class SyncAdapter
para realmente executar a sincronização.mySyncAdapter é onde a própria lógica de sincronização real é armazenada. Seu
onPerformSync()
método é chamado quando é hora de sincronizar. Eu acho que você já tem isso no lugar.4. Estabeleça uma ligação entre um tipo de conta e uma autoridade de conteúdo
Olhando novamente para AndroidManifest, aquela
<meta-data>
tag estranha em nosso serviço é a peça-chave que estabelece a ligação entre um ContentAuthority e uma conta. Ele faz referência externa a outro arquivo xml (chame-o do que quiser, algo relevante para o seu aplicativo). Vejamos sync_myapp.xml:Ok, então o que isso faz? Diz ao Android que o adaptador de sincronização que definimos (a classe que foi chamada no elemento de nome da
<service>
tag que inclui a<meta-data>
tag que faz referência a este arquivo ...) sincronizará os contatos usando uma conta do estilo com.google.Todas as suas strings contentAuthority devem corresponder e combinar com o que você está sincronizando - Deve ser uma string que você define, se estiver criando seu próprio banco de dados, ou deve usar algumas strings de dispositivo existentes se estiver sincronizando conhecido tipos de dados (como contatos ou eventos de calendário ou o que for.) O acima ("com.android.contacts") passa a ser a string ContentAuthority para dados de tipo de contato (surpresa, surpresa.)
accountType também deve corresponder a um daqueles tipos de conta conhecidos que já foram inseridos, ou deve corresponder a um que você está criando (isso envolve a criação de uma subclasse de AccountAuthenticator para obter autenticação em seu servidor ... Vale um artigo, por si só.) Novamente, "com.google" é a string definida que identifica ... credenciais da conta no estilo google.com (novamente, isso não deve ser uma surpresa).
5. Ative a sincronização em um determinado par de conta / autoridade de conteúdo
Finalmente, a sincronização deve ser ativada. Você pode fazer isso na página Contas e sincronização no painel de controle acessando seu aplicativo e marcando a caixa de seleção ao lado de seu aplicativo na conta correspondente. Como alternativa, você pode fazer isso em algum código de configuração em seu aplicativo:
Para que a sincronização ocorra, seu par conta / autoridade deve estar habilitado para sincronizar (como acima) e o sinalizador de sincronização global geral no sistema deve ser definido, e o dispositivo deve ter conectividade de rede.
Se sua sincronização de conta / autoridade ou a sincronização global estiverem desabilitadas, chamar RequestSync () tem um efeito - define um sinalizador de que a sincronização foi solicitada e será executada assim que a sincronização for habilitada.
Além disso, por mgv , definir
ContentResolver.SYNC_EXTRAS_MANUAL
como verdadeiro no pacote de extras do seu requestSync pedirá ao Android para forçar uma sincronização, mesmo se a sincronização global estiver desligada (seja respeitoso com seu usuário aqui!)Finalmente, você pode configurar uma sincronização agendada periódica, novamente com funções ContentResolver.
6. Considere as implicações de várias contas
É possível ter mais de uma conta do mesmo tipo (duas contas @ gmail.com configuradas em um dispositivo ou duas contas do Facebook, ou duas contas do Twitter, etc ...). Você deve considerar as implicações do aplicativo ao fazer isso. .. Se você tem duas contas, provavelmente não deseja sincronizar as duas nas mesmas tabelas do banco de dados. Talvez você precise especificar que apenas um pode estar ativo por vez, esvaziar as tabelas e sincronizar novamente se você trocar de conta. (por meio de uma página de propriedade que consulta quais contas estão presentes). Talvez você crie um banco de dados diferente para cada conta, talvez tabelas diferentes, talvez uma coluna-chave em cada tabela. Todo aplicativo específico e digno de alguma reflexão.
ContentResolver.setIsSyncable(Account account, String authority, int syncable)
pode ser de interesse aqui.setSyncAutomatically()
controla se um par de conta / autoridade é verificado oudesmarcado , enquantosetIsSyncable()
fornece uma maneira de desmarcar e esmaecer a linha para que o usuário não possa ativá-la. Você pode definir uma conta sincronizável e a outra não sincronizável (desativada).7. Esteja ciente de ContentResolver.notifyChange ()
Uma coisa complicada.
ContentResolver.notifyChange()
é uma função usada porContentProvider
s para notificar o Android de que o banco de dados local foi alterado. Isso tem duas funções, primeiro, fará com que os cursores seguindo esse conteúdo uri sejam atualizados e, por sua vez, consulte novamente e invalide e redesenhe umListView
, etc ... É muito mágico, o banco de dados muda e vocêListView
apenas atualiza automaticamente. Impressionante. Além disso, quando o banco de dados muda, o Android irá solicitar a sincronização para você, mesmo fora de sua programação normal, para que essas alterações sejam retiradas do dispositivo e sincronizadas com o servidor o mais rápido possível. Também incrível.Porém, há um caso extremo. Se você puxar do servidor e enviar uma atualização para o
ContentProvider
, ele será devidamente chamadonotifyChange()
e o Android dirá: "Oh, alterações no banco de dados, melhor colocá-las no servidor!" (Doh!) Bem escritoContentProviders
fará alguns testes para ver se as mudanças vieram da rede ou do usuário, e definirá osyncToNetwork
sinalizador booleano como falso em caso afirmativo, para evitar esse desperdício de sincronização dupla. Se você estiver alimentando dados em umContentProvider
, cabe a você descobrir como fazer isso funcionar - caso contrário, você acabará sempre executando duas sincronizações quando apenas uma for necessária.8. Sinta-se feliz!
Depois de ter todos esses metadados xml no lugar e a sincronização ativada, o Android saberá como conectar tudo para você e a sincronização deve começar a funcionar. Nesse ponto, muitas coisas legais simplesmente se encaixarão no lugar e parecerão mágica. Aproveitar!
fonte
ContentResolver.SYNC_EXTRAS_MANUAL
conjunto para verdadeiro ao pacote de extras e você forçará a sincronização :)Eu estava ligando
setIsSyncable
depois dosetAuthToken
método AccountManager . Mas asetAuthToken
função retornada antessetIsSyncable
foi alcançada. Depois que o pedido mudou, tudo funcionou bem!fonte