-
Notifications
You must be signed in to change notification settings - Fork 3
SyncAdapter
El SyncAdapter es la componente de la aplicación que encapsulará el código para transferir datos entre el dispositivo y un servidor. Basado en la configuración que le des el SyncAdapter ejecutará el código de sincronización. Para añadir dejar el SyncAdapter funcionando (al fin) necesitamos los siguientes componentes:
-
Clase SyncAdapter: contiene tu código de sincronización en una interface compatible con el framework sync adapter.
-
Bound service: permite al framework correr el código en el tu clase sync adapter.
-
Archivo de metadata xml: contiene información de tu sync adapter. El framework lee esta información para saber como calendarizar y cargar tu transferencia de datos.
-
Declaraciones en manifest: declara el bound service y apunta a datos específicos de la metadata del sync adapter xml.
Debemos crear una nueva clase que extienda de
AbstractThreadedSyncAdapter
, la clase base del sync adapter. También
debemos definir constructores para la clase e implementar el método
donde se definen las tareas de sincronización.
En los constructores de la clase que extienda a
AbstractThreadedSyncAdapter
(en este caso la llamaremos SyncAdapter)
debemos hacer un proceso muy similar a la configuración que se hace
cuando se está creando una actividad. Se debe hacer el setup del sync
adapter, al igual que en el Activity.onCreate()
. Por ejemplo, nosotros
utilizaremos el ContentProvider
para hacer la sincronización de datos,
por lo que es conveniente obtener una referencia a ContentResolve
r en el
constructor. Por ejemplo en nuestro caso quedaría como sigue:
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private ContentResolver mContentResolver;
private String students;
private String token;
private AccountManager mAccountManager;
...
public SyncAdapter (Context context, boolean autoInitialize){
super(context, autoInitialize);
this.mContentResolver = context.getContentResolver();
mAccountManager = AccountManager.get(context);
}
...
}
El método onPerformSync()
es el método que el framework llama para hacer
la sincronización de datos. Por lo tanto acá debes implementar toda la
lógica de tu sincronización. Los parámetros de onPerformSync()
son:
-
Account: cuenta asociada a la sincronización.
-
Extras: Bundle que contiene flags con información mandada el evento que desencadenó la sincronización.
-
Authority: la authority del content provider que implementamos.
-
ContentProviderClient: es una clase con algunas funcionalidades de
ContentResolver
y que nos permite interactuar con elContentProvider
identificado por el authority que viene en los argumentos. Si no se quiere utilizar se puede ignorar. -
SyncResult: objeto para mandar información al sync adapter framework.
El código para sincronizar datos que utilizamos en la app de ejemplo no tiene mucha importancia, pero es el siguiente:
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
// No importa que el thread se bloquee ya que es asincronico.
token = mAccountManager.blockingGetAuthToken(account, "normal" , true);
// Obtiene datos del servidor
ArrayList<String> results = performRequest(url, "GET");
// Sincroniza los datos
updateData(results, "GET");
// Manejo de errores
} catch (IOException e) {
e.printStackTrace();
} catch ...
...
}
Dentro de este método debes hacer las siguientes cosas:
-
Conectarse al servidor
-
Descargar y subir datos
-
Manejar conflictos y determinar como sincronizar los datos
-
Cerrar conexiones y limpiar archivos temporales y caché
Ahora debemos enlazar nuestro SyncAdapter al framework sync adapter para
que este tenga acceso a nuestro código. Para eso usaremos un bound
service, al igual que en el authenticator. Este servicio pasará un
Binder especial al framework. Con este Binder el framework puede invocar
a nuestro onPerformSync()
. El código sería como sigue:
public class SyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
@Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
...
}
Ahora debemos poner a disposición del framework la metadata necesaria que describe el componente y que provee algunos flags extras. Se especifica el tipo de cuenta que creaste para el tu SyncAdapter, declara un authority del content provider asociado con la app, controla una parte de la interfaz de usuario relacionada con el sync adapter y declara otros flags.
Debemos crear nuestro archivo syncadapter.xml
en /res/xml. El contenido
es como sigue:
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.example.andres.myapplication.provider"
android:accountType="com.example.andres.myapplication"
android:userVisible="true" // dice que es visible en configuraciones del telefono
android:allowParallelSyncs="false" // multiples instancias de sync adapter no pueden correr
android:isAlwaysSyncable="true" // puede sincronizar en cualquier momento que hayas dicho
android:supportsUploading="true"/>
Tenemos que declarar el servicio que creamos y agregar los permisos necesarios para que el sync adapter pueda correr adecuadamente. Los permisos necesarios son los siguientes:
-
android.permission.INTERNET
-
android.permission.READ_SYNC_SETTINGS
: permite leer el estado de la configuración del sync adapter. -
android.permission.WRITE_SYNC_SETTINGS
: permite a tu app controlar la configuración del sync adapter. -
android.permission.AUTHENTICATE_ACCOUNTS
: ya deberías tener esta desde que creamos el Authenticator.
Además, el servicio queda declarado como sigue:
<service
android:name=".Services.SyncService"
android:exported="true" // permite a procesos externos a tu app acceder al servicio
android:process=":sync"> // le dice al sistema que corra el servicio en un proceso global llamado sync
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
Siguiente: Correr el SyncAdapter.