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.
Podczas poprzednich lekcji omówiliśmy, jak utworzyć komponent adaptera synchronizacji, zawiera kod transferu danych oraz sposób dodawania dodatkowych komponentów, które umożliwiają podłącz adapter synchronizacji do systemu. Masz teraz wszystko, czego potrzebujesz do zainstalowania aplikacji, zawiera adapter synchronizacji, ale żaden z widocznego kodu nie uruchamia adaptera.
Spróbuj uruchomić adapter synchronizacji zgodnie z harmonogramem lub jako pośredni efekt . Możesz na przykład chcieć, aby adapter synchronizacji działał w regularnych odstępach czasu, zarówno po w konkretnym przedziale czasu lub o określonej porze dnia. Możesz też uruchomić synchronizację adaptera, gdy pojawią się zmiany w danych zapisanych na urządzeniu. Należy unikać uruchamiania może być bezpośrednim wynikiem działania użytkownika, bo w efekcie nie uzyskasz pełnego z możliwości planowania platformy adaptera synchronizacji. Unikaj na przykład: przez udostępnienie w interfejsie przycisku odświeżania.
Dostępne są te opcje uruchomienia adaptera synchronizacji:
- Gdy dane serwera się zmienią
- uruchomić adapter synchronizacji w odpowiedzi na komunikat z serwera, wskazując, że uległy zmianie. Ta opcja umożliwia odświeżanie danych z serwera na urządzenie bez pogarszania wydajności czy marnowania czasu pracy na baterii przez odpytywanie serwera.
- Gdy dane urządzenia ulegną zmianie
- Używaj adaptera synchronizacji w przypadku zmiany danych na urządzeniu. Ta opcja umożliwia wysyłanie zmodyfikowanych danych z urządzenia na serwer i jest szczególnie przydatne, gdy trzeba zagwarantować, aby serwer zawsze miał najnowsze dane urządzenia. Ta opcja pozwala w prosty sposób jeśli faktycznie przechowujesz dane u dostawcy treści. Jeśli używasz fragmentu dostawców treści, wykrycie zmian w danych może być trudniejsze.
- W regularnych odstępach czasu
- Uruchom adapter synchronizacji po upłynięciu wybranego interwału lub z określonym czasem codziennie.
- Na żądanie
- Uruchom adapter synchronizacji w odpowiedzi na działanie użytkownika. Jednak aby zapewnić użytkownikom należy korzystać głównie z jednej z bardziej zautomatyzowanych opcji. Za pomocą automatyczne opcje, oszczędzasz baterię i zasoby sieciowe.
W pozostałej części tej lekcji znajdziesz bardziej szczegółowe informacje o każdej z tych opcji.
Uruchamianie adaptera synchronizacji w przypadku zmiany danych serwera
Jeśli Twoja aplikacja przesyła dane z serwera, a dane serwera często się zmieniają, możesz
adaptera synchronizacji do pobierania plików w odpowiedzi na zmiany danych. Aby uruchomić adapter synchronizacji, upewnij się,
serwer wysyła specjalną wiadomość do BroadcastReceiver
w Twojej aplikacji.
W odpowiedzi na ten komunikat wywołaj ContentResolver.requestSync()
, aby zasygnalizować platformie adaptera synchronizacji, że ma uruchomić
lub adapter synchronizacji.
Google Cloud Messaging (GCM) zapewnia zarówno
serwera i urządzenia wymagane do działania tego systemu przesyłania wiadomości. Korzystanie z GCM do aktywacji
jest bardziej niezawodne i wydajniejsze niż odpytywanie serwerów w celu sprawdzenia stanu. Podczas ankiety
wymaga obiektu Service
, który jest zawsze aktywny, GCM używa
Urządzenie BroadcastReceiver
aktywowane po odebraniu wiadomości. Podczas ankiety
w regularnych odstępach czasu korzysta z baterii, nawet jeśli aktualizacje nie są dostępne, GCM wysyła tylko wiadomości
wiadomości w razie potrzeby.
Uwaga: jeśli używasz GCM, aby aktywować adapter synchronizacji za pomocą komunikatu do wszystkich urządzeń, na których jest zainstalowana Twoja aplikacja, pamiętaj, że otrzymują oni Twoją wiadomość na adres mniej więcej w tym samym czasie. Taka sytuacja może spowodować uruchomienie wielu instancji adaptera synchronizacji jednocześnie, powodując przeciążenie serwera i sieci. Aby uniknąć takiej sytuacji w przypadku transmisji dla wszystkich urządzeń, warto rozważyć odłożenie rozpoczęcia adaptera synchronizacji na pewien czas który jest unikalny dla każdego urządzenia.
Fragment kodu poniżej pokazuje, jak uruchomić
requestSync()
w odpowiedzi na
przychodząca wiadomość GCM:
Kotlin
... // Constants // Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Account type const val ACCOUNT_TYPE = "com.example.android.datasync" // Account const val ACCOUNT = "default_account" // Incoming Intent key for extended data const val KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST" ... class GcmBroadcastReceiver : BroadcastReceiver() { ... override fun onReceive(context: Context, intent: Intent) { // Get a GCM object instance val gcm: GoogleCloudMessaging = GoogleCloudMessaging.getInstance(context) // Get the type of GCM message val messageType: String? = gcm.getMessageType(intent) /* * Test the message type and examine the message contents. * Since GCM is a general-purpose messaging system, you * may receive normal messages that don't require a sync * adapter run. * The following code tests for a a boolean flag indicating * that the message is requesting a transfer from the device. */ if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE == messageType && intent.getBooleanExtra(KEY_SYNC_REQUEST, false)) { /* * Signal the framework to run your sync adapter. Assume that * app initialization has already created the account. */ ContentResolver.requestSync(mAccount, AUTHORITY, null) ... } ... } ... }
Java
public class GcmBroadcastReceiver extends BroadcastReceiver { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account type public static final String ACCOUNT_TYPE = "com.example.android.datasync"; // Account public static final String ACCOUNT = "default_account"; // Incoming Intent key for extended data public static final String KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST"; ... @Override public void onReceive(Context context, Intent intent) { // Get a GCM object instance GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); // Get the type of GCM message String messageType = gcm.getMessageType(intent); /* * Test the message type and examine the message contents. * Since GCM is a general-purpose messaging system, you * may receive normal messages that don't require a sync * adapter run. * The following code tests for a a boolean flag indicating * that the message is requesting a transfer from the device. */ if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType) && intent.getBooleanExtra(KEY_SYNC_REQUEST)) { /* * Signal the framework to run your sync adapter. Assume that * app initialization has already created the account. */ ContentResolver.requestSync(mAccount, AUTHORITY, null); ... } ... } ... }
Uruchamianie adaptera synchronizacji w przypadku zmiany danych dostawcy treści
Jeśli Twoja aplikacja zbiera dane od dostawcy treści i chcesz aktualizować serwer za każdym razem,
zaktualizujesz dostawcę, możesz skonfigurować aplikację tak, aby automatycznie uruchamiała adapter synchronizacji. Do zrobienia
rejestrujesz obserwatora dostawcy treści. Gdy dane u dostawcy treści
zmian, system dostawcy treści zwraca się do obserwatora. W obserwatorze wywołaj funkcję
requestSync()
, aby wskazać, że platforma ma uruchomić
adaptera synchronizacji.
Uwaga: jeśli korzystasz z usług dostawcy treści, nie masz żadnych danych w
dostawca treści oraz onChange()
jest
nigdy nie został wywołany. W takim przypadku musisz udostępnić własny mechanizm wykrywania zmian
danych urządzenia. Odpowiada on również za wywoływanie
requestSync()
po zmianie danych.
Aby utworzyć obserwatora dla dostawcy treści, rozszerz zajęcia
ContentObserver
i zaimplementuj obie jego formy
Metoda onChange()
. W
onChange()
, zadzwoń
requestSync()
, aby uruchomić adapter synchronizacji.
Aby zarejestrować obserwatora, przekaż go jako argument w wywołaniu funkcji
registerContentObserver()
W
w tym wywołaniu, musisz też dla danych, które chcesz oglądać, musisz przekazać URI treści. Treść
dostawca platformy porównuje identyfikator URI zegarka z identyfikatorami URI treści przekazywanymi jako argumenty do
ContentResolver
metod modyfikujących dostawcę, takich jak
ContentResolver.insert()
Jeśli do siebie pasują,
implementacja funkcji ContentObserver.onChange()
.
Poniższy fragment kodu pokazuje, jak zdefiniować ContentObserver
wywołujący funkcję requestSync()
, gdy tabela
zmiany:
Kotlin
// Constants // Content provider scheme const val SCHEME = "content://" // Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Path for the content provider table const val TABLE_PATH = "data_table" ... class MainActivity : FragmentActivity() { ... // A content URI for the content provider's data table private lateinit var uri: Uri // A content resolver for accessing the provider private lateinit var mResolver: ContentResolver ... inner class TableObserver(...) : ContentObserver(...) { /* * Define a method that's called when data in the * observed content provider changes. * This method signature is provided for compatibility with * older platforms. */ override fun onChange(selfChange: Boolean) { /* * Invoke the method signature available as of * Android platform version 4.1, with a null URI. */ onChange(selfChange, null) } /* * Define a method that's called when data in the * observed content provider changes. */ override fun onChange(selfChange: Boolean, changeUri: Uri?) { /* * Ask the framework to run your sync adapter. * To maintain backward compatibility, assume that * changeUri is null. */ ContentResolver.requestSync(account, AUTHORITY, null) } ... } ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Get the content resolver object for your app mResolver = contentResolver // Construct a URI that points to the content provider data table uri = Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .path(TABLE_PATH) .build() /* * Create a content observer object. * Its code does not mutate the provider, so set * selfChange to "false" */ val observer = TableObserver(false) /* * Register the observer for the data table. The table's path * and any of its subpaths trigger the observer. */ mResolver.registerContentObserver(uri, true, observer) ... } ... }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider scheme public static final String SCHEME = "content://"; // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Path for the content provider table public static final String TABLE_PATH = "data_table"; // Account public static final String ACCOUNT = "default_account"; // Global variables // A content URI for the content provider's data table Uri uri; // A content resolver for accessing the provider ContentResolver mResolver; ... public class TableObserver extends ContentObserver { /* * Define a method that's called when data in the * observed content provider changes. * This method signature is provided for compatibility with * older platforms. */ @Override public void onChange(boolean selfChange) { /* * Invoke the method signature available as of * Android platform version 4.1, with a null URI. */ onChange(selfChange, null); } /* * Define a method that's called when data in the * observed content provider changes. */ @Override public void onChange(boolean selfChange, Uri changeUri) { /* * Ask the framework to run your sync adapter. * To maintain backward compatibility, assume that * changeUri is null. */ ContentResolver.requestSync(mAccount, AUTHORITY, null); } ... } ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Get the content resolver object for your app mResolver = getContentResolver(); // Construct a URI that points to the content provider data table uri = new Uri.Builder() .scheme(SCHEME) .authority(AUTHORITY) .path(TABLE_PATH) .build(); /* * Create a content observer object. * Its code does not mutate the provider, so set * selfChange to "false" */ TableObserver observer = new TableObserver(false); /* * Register the observer for the data table. The table's path * and any of its subpaths trigger the observer. */ mResolver.registerContentObserver(uri, true, observer); ... } ... }
Okresowo uruchamiaj adapter synchronizacji
Możesz okresowo uruchamiać adapter synchronizacji, ustawiając odstęp między uruchomieniami uruchamiając go o określonych porach dnia lub w obu tych godzinach. Korzystanie z adaptera synchronizacji okresowo pozwala uzyskać mniej więcej zgodność z częstotliwością aktualizacji serwera.
Podobnie można przesyłać dane z urządzenia, gdy serwer jest względnie bezczynny, przez Zaplanuj uruchamianie adaptera synchronizacji w nocy. Większość użytkowników pozostawia włączone urządzenie i podłączone do zasilania w nocy, więc ten czas jest zwykle dostępny. Ponadto na urządzeniu nie są uruchomione inne zadania w tym samym czasie co adapter synchronizacji. Jeśli jednak wybierzesz takie podejście, każdy z urządzeń rozpoczyna przesyłanie danych w nieco innym czasie. Jeśli wszystkie urządzenia obsługują synchronizacji, prawdopodobnie przeciążenie serwera i operatora sieci komórkowej sieci.
Ogólnie okresowe uruchomienia są sensowne, jeśli użytkownicy nie potrzebują błyskawicznej aktualizacji, lecz oczekują . Uruchomienia okresowe są też przydatne, jeśli chcesz zrównoważyć dostępność dostęp do aktualnych danych i umożliwia korzystanie z mniejszych adapterów synchronizacji, które nie przeciążają urządzenia. i zasobami Google Cloud.
Aby używać adaptera synchronizacji w regularnych odstępach czasu, wywołaj
addPeriodicSync()
Spowoduje to zaplanowanie
adapter synchronizacji uruchamiał się po upływie określonego czasu. Ponieważ platforma adaptera synchronizacji
uwzględnia inne przypadki użycia adaptera synchronizacji i stara się zmaksymalizować żywotność baterii,
czas ten może się różnić o kilka sekund. Platforma nie uruchomi adaptera synchronizacji, jeśli
Sieć jest niedostępna.
Zwróć uwagę, że addPeriodicSync()
nie:
korzystanie z adaptera synchronizacji o określonej porze dnia. Aby korzystać z adaptera synchronizacji mniej więcej
o tej samej porze każdego dnia, jako aktywatora użyj cyklicznego alarmu. Powtarzające się alarmy są opisane poniżej
znajdziesz w dokumentacji referencyjnej AlarmManager
. Jeśli używasz tagu
metoda setInexactRepeating()
do ustawienia
dla czynników wpływających na porę dnia, które różnią się od siebie. Mimo to należy wybierać losowo godzinę rozpoczęcia,
upewnij się, że adapter synchronizacji uruchamia się z różnych urządzeń w rozłożonym czasie.
Metoda addPeriodicSync()
nie
wyłącz setSyncAutomatically()
,
więc możesz przeprowadzić wiele synchronizacji w krótkim czasie. Poza tym tylko kilka
flagi sterujące adaptera synchronizacji są dozwolone w wywołaniu funkcji
addPeriodicSync()
; flagi, które
niedozwolone są opisane we wskazanej dokumentacji dotyczącej
addPeriodicSync()
Fragment kodu poniżej pokazuje, jak zaplanować okresowe uruchamianie adaptera synchronizacji:
Kotlin
// Content provider authority const val AUTHORITY = "com.example.android.datasync.provider" // Account const val ACCOUNT = "default_account" // Sync interval constants const val SECONDS_PER_MINUTE = 60L const val SYNC_INTERVAL_IN_MINUTES = 60L const val SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE ... class MainActivity : FragmentActivity() { ... // A content resolver for accessing the provider private lateinit var mResolver: ContentResolver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Get the content resolver for your app mResolver = contentResolver /* * Turn on periodic syncing */ ContentResolver.addPeriodicSync( mAccount, AUTHORITY, Bundle.EMPTY, SYNC_INTERVAL) ... } ... }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account public static final String ACCOUNT = "default_account"; // Sync interval constants public static final long SECONDS_PER_MINUTE = 60L; public static final long SYNC_INTERVAL_IN_MINUTES = 60L; public static final long SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE; // Global variables // A content resolver for accessing the provider ContentResolver mResolver; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Get the content resolver for your app mResolver = getContentResolver(); /* * Turn on periodic syncing */ ContentResolver.addPeriodicSync( mAccount, AUTHORITY, Bundle.EMPTY, SYNC_INTERVAL); ... } ... }
Uruchamiaj adapter synchronizacji na żądanie
Używanie adaptera synchronizacji w odpowiedzi na żądanie użytkownika to najmniej preferowana strategia do korzystania z adaptera synchronizacji. Platforma została zaprojektowana tak, aby oszczędzać baterię gdy zostanie uruchomione adaptery synchronizacji zgodnie z harmonogramem. Opcje uruchamiające synchronizację w odpowiedzi na dane zmiany efektywnie wykorzystują baterię, ponieważ jest ona używana do pozyskiwania nowych danych.
Dla porównania: jeśli użytkownik ma uruchomić synchronizację na żądanie, synchronizacja przebiega automatycznie, jest nieefektywne wykorzystanie zasobów sieciowych i energii. Synchronizacja na żądanie pozwala użytkownikom zażądać synchronizacji, nawet jeśli nie ma dowodów na to, że dane uległy zmianie, a następnie nie odświeża danych to nieefektywne wykorzystanie baterii. Ogólnie aplikacja powinna: używać innych sygnałów do aktywowania synchronizacji lub planowania ich w regularnych odstępach czasu bez danych wejściowych użytkownika.
Jeśli jednak nadal chcesz uruchamiać adapter synchronizacji na żądanie, ustaw flagi adaptera synchronizacji dla parametru
ręczne uruchomienie adaptera synchronizacji, a następnie wywołanie
ContentResolver.requestSync()
Uruchamiaj transfery na żądanie z tymi flagami:
-
SYNC_EXTRAS_MANUAL
-
Wymusza synchronizację ręczną. Platforma adaptera synchronizacji ignoruje istniejące ustawienia,
np. flagę ustawioną przez
setSyncAutomatically()
. -
SYNC_EXTRAS_EXPEDITED
- Wymusza natychmiastowe rozpoczęcie synchronizacji. Jeśli nie skonfigurujesz tej opcji, system może czekać przed uruchomieniem żądania synchronizacji, ponieważ system próbuje zoptymalizować wykorzystanie baterii przez planując wiele żądań w krótkim czasie.
Fragment kodu pokazujący, jak zadzwonić
requestSync()
w odpowiedzi na przycisk
kliknij:
Kotlin
// Constants // Content provider authority val AUTHORITY = "com.example.android.datasync.provider" // Account type val ACCOUNT_TYPE = "com.example.android.datasync" // Account val ACCOUNT = "default_account" ... class MainActivity : FragmentActivity() { ... // Instance fields private lateinit var mAccount: Account ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... /* * Create the placeholder account. The code for CreateSyncAccount * is listed in the lesson Creating a Sync Adapter */ mAccount = createSyncAccount() ... } /** * Respond to a button click by calling requestSync(). This is an * asynchronous operation. * * This method is attached to the refresh button in the layout * XML file * * @param v The View associated with the method call, * in this case a Button */ fun onRefreshButtonClick(v: View) { // Pass the settings flags by inserting them in a bundle val settingsBundle = Bundle().apply { putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true) } /* * Request the sync for the default account, authority, and * manual sync settings */ ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle) }
Java
public class MainActivity extends FragmentActivity { ... // Constants // Content provider authority public static final String AUTHORITY = "com.example.android.datasync.provider"; // Account type public static final String ACCOUNT_TYPE = "com.example.android.datasync"; // Account public static final String ACCOUNT = "default_account"; // Instance fields Account mAccount; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... /* * Create the placeholder account. The code for CreateSyncAccount * is listed in the lesson Creating a Sync Adapter */ mAccount = CreateSyncAccount(this); ... } /** * Respond to a button click by calling requestSync(). This is an * asynchronous operation. * * This method is attached to the refresh button in the layout * XML file * * @param v The View associated with the method call, * in this case a Button */ public void onRefreshButtonClick(View v) { // Pass the settings flags by inserting them in a bundle Bundle settingsBundle = new Bundle(); settingsBundle.putBoolean( ContentResolver.SYNC_EXTRAS_MANUAL, true); settingsBundle.putBoolean( ContentResolver.SYNC_EXTRAS_EXPEDITED, true); /* * Request the sync for the default account, authority, and * manual sync settings */ ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle); }