Uruchamianie adaptera synchronizacji

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);
    }