Uwaga: zalecamy korzystanie z WorkManagera jako zalecane rozwiązanie w większości przypadków użycia związanych z przetwarzaniem w tle. Zapoznaj się z przewodnika po przetwarzaniu w tle, by dowiedzieć się, które rozwiązanie jest dla Ciebie najlepsze.
Komponent adaptera synchronizacji w aplikacji umieszcza kod dla zadań, które są przenoszone między urządzeniem a serwerem. Na podstawie harmonogramu i reguł podanych przez Ciebie w aplikacji, platforma adaptera synchronizacji uruchamia kod komponentu adaptera synchronizacji. Aby dodać adapter do aplikacji, musisz dodać te elementy:
- Synchronizuj klasę adaptera.
- Klasa, która opakowuje kod przenoszenia danych w interfejsie zgodnym z adapterem synchronizacji. platformy.
-
Łączenie z
Service
. - Komponent umożliwiający platformie adaptera synchronizacji uruchomienie kodu w adapterze synchronizacji zajęcia.
- Synchronizacja pliku XML metadanych adaptera.
- Plik zawierający informacje o adapterze synchronizacji. Platforma odczytuje ten plik do dowiedz się, jak wczytać i zaplanować przenoszenie danych.
- Deklaracje w manifeście aplikacji.
- Kod XML z deklaracją powiązanej usługi i wskazującą synchronizację metadanych związanych z adapterem.
Z tej lekcji dowiesz się, jak zdefiniować te elementy.
Tworzenie klasy adaptera synchronizacji
W tej części lekcji dowiesz się, jak utworzyć klasę adaptera synchronizacji, która zawiera kod transferu danych. Utworzenie klasy obejmuje rozszerzenie klasy bazowej adaptera synchronizacji, zdefiniowanie konstruktory dla klasy i implementowanie metody, w której definiuje się przesyłanie danych zadania.
Rozszerz podstawową klasę adaptera synchronizacji
Aby utworzyć komponent adaptera synchronizacji, zacznij od rozszerzenia
AbstractThreadedSyncAdapter
i tworzeniu jego konstruktorów. Użyj
konstruktory do uruchamiania zadań konfiguracyjnych za każdym razem, gdy na podstawie komponentu adaptera synchronizacji jest tworzony
tak samo jak za pomocą usługi Activity.onCreate()
działania. Jeśli na przykład aplikacja korzysta z usług dostawcy treści do przechowywania danych, użyj konstruktorów
aby pobrać instancję ContentResolver
. Ponieważ druga forma atrybutu
został dodany do platformy Androida 3.0 w celu obsługi parallelSyncs
, musisz utworzyć dwie formy konstruktora, aby zachować zgodność.
Uwaga: platforma adaptera synchronizacji jest przeznaczona do współpracy z adapterem synchronizacji które są instancjami pojedynczymi. Tworzenie instancji komponentu adaptera synchronizacji jest omówione bardziej szczegółowo w sekcji Powiąż adapter synchronizacji z platformą.
Poniższy przykład pokazuje, jak zaimplementować
AbstractThreadedSyncAdapter
i jego konstruktory:
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(); ... }
Dodawanie kodu przenoszenia danych
Komponent adaptera synchronizacji nie wykonuje automatycznego przenoszenia danych. Zamiast tego
otacza kod transferu danych, aby platforma adaptera synchronizacji mogła uruchamiać
i przesyłanie danych w tle bez udziału aplikacji. Gdy platforma jest gotowa
synchronizacji danych aplikacji wywołuje implementację metody
onPerformSync()
Aby ułatwić przenoszenie danych z głównego kodu aplikacji do komponentu adaptera synchronizacji,
wywołania platformy adaptera synchronizacji
onPerformSync()
z
następujące argumenty:
- Konto
-
Obiekt
Account
powiązany ze zdarzeniem, które wywołało lub adapter synchronizacji. Jeśli serwer nie korzysta z kont, nie musisz używać metody w tym obiekcie. - Dodatkowe treści
-
Pole
Bundle
zawierające flagi wysłane przez zdarzenie, które uruchomiło synchronizację przejściówkę. - Wiarygodność
- Uprawnienia dostawcy treści w systemie. Aplikacja musi mieć dostęp do tych elementów: u tego dostawcy. Zwykle urząd odpowiada dostawcy treści w Twojej aplikacji. .
- Klient dostawcy treści
-
ContentProviderClient
dla dostawcy treści wskazywanego przez parametr argumentu władzy.ContentProviderClient
to lekka wersja publiczna z dostawcą treści. Ma te same podstawowe funkcje coContentResolver
Jeśli korzystasz z usług dostawcy treści do przechowywania danych aplikacji, możesz połączyć się z dostawcą za pomocą tego obiektu. W przeciwnym razie możesz zignorować . - Wynik synchronizacji
-
Obiekt
SyncResult
używany do wysyłania informacji do synchronizacji i różnic między nimi.
Ten fragment kodu przedstawia ogólną strukturę
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. */ }
Chociaż rzeczywiste wdrożenie
onPerformSync()
dotyczy
wymagań aplikacji w zakresie synchronizacji danych i protokołów połączeń z serwerem, istnieje kilka
ogólne zadania, które powinna wykonać Twoja implementacja:
- Łączenie z serwerem
- Można przyjąć, że w momencie rozpoczęcia przenoszenia danych sieć jest dostępna, jednak platforma adaptera synchronizacji nie łączy się automatycznie z serwerem.
- Pobieranie i przesyłanie danych
- Adapter synchronizacji nie automatyzuje żadnych zadań związanych z przesyłaniem danych. Jeśli chcesz pobrać z serwera i zapisują je u dostawcy treści, musisz dostarczyć kod, który pobiera dane, pobiera je i wstawia u dostawcy. Podobnie, jeśli chcesz aby wysłać dane na serwer, trzeba je odczytać z pliku, bazy danych lub dostawcy i wysłać z niezbędną prośbą o przesłanie. Musisz też radzić sobie z błędami sieciowymi, które występują podczas korzystania trwa przesyłanie danych.
- radzić sobie z konfliktami danych i określać ich aktualność;
- Adapter synchronizacji nie obsługuje automatycznie konfliktów między danymi na serwerze a danymi na urządzeniu. Ponadto nie wykrywa automatycznie, czy dane na serwerze są nowsze niż z danymi na urządzeniu lub odwrotnie. Zamiast tego musisz utworzyć własne algorytmy dla radzenia sobie z taką sytuacją.
- Wyczyść dane.
- Zawsze zamykaj połączenia z serwerem i wyczyść pliki tymczasowe oraz pamięci podręczne na końcu i przeniesieniu danych.
Uwaga: działa platforma adaptera synchronizacji
onPerformSync()
w:
, dzięki czemu nie musisz konfigurować
własnego przetwarzania w tle.
Oprócz wykonywania zadań związanych z synchronizacją warto też połączyć
zadania związane z siecią i dodawanie ich do
onPerformSync()
Skupienie na tej metodzie wszystkich zadań sieciowych pozwala oszczędzać energię baterii
potrzebne do uruchamiania i zatrzymywania interfejsów sieci. Więcej informacji o zwiększaniu dostępu do sieci
znajdziesz w lekcji Przenoszenie danych bez wyczerpywania baterii, w której omawiamy zagadnienia związane z dostępem do różnych sieci
które można uwzględnić w kodzie przenoszenia danych.
Powiąż adapter synchronizacji ze platformą
Kod transferu danych jest teraz zawarty w elemencie adaptera synchronizacji, ale masz też
aby zapewnić platformie dostęp do kodu. Aby to zrobić, musisz utworzyć powiązanie
Service
, który przekazuje specjalny obiekt powiązania Androida z adaptera synchronizacji.
do struktury. Za pomocą tego obiektu powiązania platforma może wywoływać metodę
onPerformSync()
i
i przekazywać do niego dane.
Utwórz wystąpienie komponentu adaptera synchronizacji jako pojedynczy element
Metoda onCreate()
usługi. Przez utworzenie instancji
komponentu w onCreate()
, opóźnisz
i tworzy go do momentu uruchomienia usługi, co ma miejsce, gdy platforma po raz pierwszy próbuje uruchomić
przesyłanie danych. Wystąpienie komponentu należy utworzyć w sposób zapewniający ochronę wątków na wypadek, gdyby synchronizacja
platforma adaptacyjna umieszcza w kolejce wiele uruchomień adaptera synchronizacji w odpowiedzi na aktywatory lub
harmonogram.
Na przykład ten fragment kodu pokazuje, jak utworzyć klasę, która umieszcza
bound (Service
), tworzy instancję komponentu adaptera synchronizacji i pobiera
Obiekt powiązania Androida:
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(); } }
Uwaga: aby zobaczyć bardziej szczegółowy przykład usługi powiązanej z adapterem synchronizacji, znajdziesz w przykładowej aplikacji.
Dodaj konto wymagane przez platformę
Platforma adaptera synchronizacji wymaga, aby każdy adapter synchronizacji miał typ konta. Zadeklarowano
wartość typu konta w sekcji,
Dodaj plik metadanych z aplikacji Authenticator. Teraz musisz skonfigurować ten rodzaj konta w
System Android. Aby skonfigurować rodzaj konta, dodaj konto zastępcze, które korzysta z jego typu
dzwoniąc pod numer addAccountExplicitly()
.
Najlepszym miejscem do wywołania tej metody jest
Metoda onCreate()
Twojej aplikacji
podczas otwierania. Fragment kodu, który pokazuje, jak to zrobić:
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. */ } } ... }
Dodaj plik metadanych adaptera synchronizacji
Aby podłączyć komponent adaptera synchronizacji do platformy, musisz udostępnić platformę
z metadanymi, które opisują komponent i zawierają dodatkowe flagi. Metadane określają,
typ konta utworzony na potrzeby adaptera synchronizacji deklaruje urząd dostawcy treści
powiązanego z aplikacją – kontroluje część interfejsu systemowego związaną z adapterami synchronizacji,
i deklaruje inne flagi związane z synchronizacją. Zadeklaruj te metadane w specjalnym pliku XML przechowywanym w
katalogu /res/xml/
w projekcie aplikacji. Plikowi możesz nadać dowolną nazwę,
ale zwykle się nazywa syncadapter.xml
.
Ten plik XML zawiera jeden element XML <sync-adapter>
, który zawiera parametr
następujące atrybuty:
android:contentAuthority
-
Urząd URI dostawcy treści Jeśli został utworzony dostawca treści dla
aplikacji w poprzedniej lekcji Tworzenie dostawcy treści Stub, użyj wartości określonej dla atrybutu
atrybut
android:authorities
w elemencie<provider>
dodanym do pliku manifestu aplikacji. Ten atrybut jest omówiono to szczegółowo w sekcji Zadeklaruj dostawcę w pliku manifestu.
Jeśli przenosisz dane od dostawcy treści na serwer za pomocą adaptera synchronizacji, powinna być taka sama jak identyfikator URI treści, którego używasz w przypadku tych danych. Ta wartość jest również jednym z organów wskazanych wandroid:authorities
w elemencie<provider>
deklarującego dostawcę w manifeście aplikacji. android:accountType
-
Rodzaj konta wymagany przez platformę adaptera synchronizacji. Wartość musi być taka sama
jako wartość typu konta podaną przy tworzeniu pliku metadanych uwierzytelniania,
opisane w sekcji Dodawanie pliku metadanych Authenticator. Jest to również wartość podana w atrybucie
stała
ACCOUNT_TYPE
we fragmencie kodu w sekcji Dodaj konto wymagane przez platformę. - Atrybuty ustawień
-
-
android:userVisible
- Ustawia widoczność typu konta adaptera synchronizacji. Domyślnie atrybut ikonę i etykietę powiązaną z typem konta są widoczne w Konta w systemowej aplikacji Ustawienia, więc musisz przeprowadzić synchronizację. Adapter jest niewidoczny, chyba że masz konto lub domenę, które można łatwo powiązać z Twoją aplikacją. Nawet jeśli ustawisz swój rodzaj konta jako niewidoczny, nadal możesz zezwolić użytkownikom na kontrolować adapter synchronizacji za pomocą interfejsu w jednej z aktywności w aplikacji.
-
android:supportsUploading
-
Umożliwia przesyłanie danych do chmury. Ustaw jako
false
, jeśli Twoja aplikacja pobiera dane. -
android:allowParallelSyncs
- Umożliwia jednoczesne uruchamianie wielu wystąpień komponentu adaptera synchronizacji. Użyj go, jeśli aplikacja obsługuje wiele kont użytkowników i chcesz zezwolić na korzystanie z wielu kont użytkowników, aby przenosić dane równolegle. Ta flaga nie działa, jeśli nigdy nie jest uruchamiana wielu transferów danych.
-
android:isAlwaysSyncable
-
Wskazuje platformę adaptera synchronizacji, że może ona uruchomić adapter synchronizacji w dowolnym momencie
w wybranym przez Ciebie czasie. Jeśli chcesz automatycznie kontrolować moment synchronizacji
może działać adapter, ustaw tę flagę na
false
, a potem wywołajrequestSync()
, aby uruchomić lub adapter synchronizacji. Więcej informacji o korzystaniu z adaptera synchronizacji można znaleźć w wykładzie Uruchamianie adaptera synchronizacji
-
Poniższy przykład pokazuje kod XML adaptera synchronizacji, który korzysta z jednego konta zastępczego tylko pobieranie.
<?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"/>
Zadeklaruj adapter synchronizacji w pliku manifestu
Po dodaniu do aplikacji komponentu adaptera synchronizacji musisz poprosić o uprawnienia
związane z komponentem. Musisz zadeklarować Service
dodany przez Ciebie.
Komponent adaptera synchronizacji uruchamia kod, który przesyła dane między siecią a urządzenia, musisz poprosić o pozwolenie na dostęp do internetu. Aplikacja musi też: aby poprosić o uprawnienia do odczytu i zapisu ustawień adaptera synchronizacji, aby móc kontrolować synchronizację z innych komponentów aplikacji. Musisz również poprosić o specjalne uprawnienie, które umożliwia aplikacji używanie utworzonego komponentu uwierzytelniającego w lekcji Tworzenie aplikacji Stub Authenticator.
Aby poprosić o te uprawnienia, dodaj do pliku manifestu aplikacji te elementy jako elementy podrzędne argumentu
<manifest>
:
-
android.permission.INTERNET
- Zezwala kodowi adaptera synchronizacji na dostęp do internetu, aby mógł pobierać i przesyłać dane z urządzenia na serwer. Nie musisz ponownie dodawać tych uprawnień, jeśli: o przesłanie prośby o niego wcześniej.
-
android.permission.READ_SYNC_SETTINGS
-
Zezwala aplikacji na odczyt bieżących ustawień adaptera synchronizacji. Na przykład:
aby wywołać
getIsSyncable()
. -
android.permission.WRITE_SYNC_SETTINGS
-
Zezwala aplikacji na kontrolowanie ustawień adaptera synchronizacji. Te uprawnienia są potrzebne, aby:
ustaw używanie adaptera okresowego synchronizacji za pomocą interfejsu
addPeriodicSync()
. Te uprawnienia nie są wymagane do wywołaniarequestSync()
Aby dowiedzieć się więcej o: uruchamianie adaptera synchronizacji zawiera artykuł Uruchamianie adaptera synchronizacji.
Ten fragment kodu pokazuje, jak dodać uprawnienia:
<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>
Na koniec należy zadeklarować granicę Service
, której platforma używa do
wejść w interakcję z adapterem synchronizacji, dodaj poniższy kod XML do pliku manifestu aplikacji jako element podrzędny
z <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>
<intent-filter>
konfiguruje filtr wywoływany przez działanie intencji
android.content.SyncAdapter
, wysyłane przez system w celu uruchomienia adaptera synchronizacji. Kiedy filtr
system uruchomi powiązaną usługę, którą w tym przykładzie jest
SyncService
Atrybut
android:exported="false"
zapewnia dostęp tylko aplikacji i systemowi
Service
Atrybut
android:process=":sync"
informuje system, aby uruchomić Service
w globalnym procesie współdzielonym o nazwie
sync
Jeśli masz w aplikacji wiele adapterów synchronizacji, mogą one współdzielić ten proces.
co pozwala zmniejszyć koszty.
<meta-data>
zawiera nazwę utworzonego wcześniej pliku XML metadanych adaptera synchronizacji.
android:name
wskazuje, że metadane dotyczą platformy adaptera synchronizacji.
android:resource
określa nazwę pliku metadanych.
Masz teraz wszystkie komponenty adaptera synchronizacji. Na następnej lekcji dowiesz się, jak nakazuje platformie adaptera synchronizacji uruchamianie adaptera synchronizacji w odpowiedzi na zdarzenie lub w sposób zrównoważony.