Menjalankan adaptor sinkronisasi

Catatan: Kami merekomendasikan WorkManager sebagai solusi yang direkomendasikan untuk sebagian besar kasus penggunaan pemrosesan latar belakang. Harap lihat panduan pemrosesan di latar belakang untuk mempelajari solusi mana yang paling cocok untuk Anda.

Pada pelajaran sebelumnya di kelas ini, Anda telah mempelajari cara membuat komponen adaptor sinkronisasi yang mengenkapsulasi kode transfer data, dan cara menambahkan komponen tambahan yang memungkinkan Anda untuk sambungkan adaptor sinkronisasi ke sistem. Anda sekarang memiliki semua yang dibutuhkan untuk menginstal aplikasi yang menyertakan adaptor sinkronisasi, tetapi tidak satu pun kode yang Anda lihat benar-benar menjalankan adaptor sinkronisasi.

Anda harus mencoba menjalankan adaptor sinkronisasi berdasarkan jadwal atau sebagai hasil tidak langsung dari peristiwa. Misalnya, Anda mungkin ingin adaptor sinkronisasi berjalan sesuai jadwal rutin, baik setelah periode waktu tertentu atau pada waktu tertentu. Anda mungkin juga ingin menjalankan sinkronisasi ketika ada perubahan pada data yang disimpan di perangkat. Sebaiknya jangan menjalankan adaptor sinkronisasi sebagai hasil langsung dari tindakan pengguna, karena dengan melakukan ini Anda tidak mendapatkan manfaat dari kemampuan penjadwalan kerangka kerja adaptor sinkronisasi. Misalnya, Anda harus menghindari menyediakan tombol {i>refresh <i} di antarmuka pengguna Anda.

Anda memiliki opsi berikut untuk menjalankan adaptor sinkronisasi:

Saat data server berubah
Jalankan adaptor sinkronisasi sebagai respons terhadap pesan dari server, yang menunjukkan bahwa solusi berbasis server data telah berubah. Opsi ini memungkinkan Anda memuat ulang data dari server ke perangkat tanpa menurunkan kinerja atau membuang-buang masa pakai baterai dengan melakukan polling pada server.
Saat data perangkat berubah
Jalankan adaptor sinkronisasi saat data berubah di perangkat. Opsi ini memungkinkan Anda mengirim mengubah data dari perangkat ke server, dan sangat berguna jika Anda perlu memastikan bahwa server selalu memiliki data perangkat terbaru. Opsi ini mudah untuk terapkan jika Anda benar-benar menyimpan data di penyedia konten. Jika Anda menggunakan stub penyedia konten, mendeteksi perubahan data mungkin lebih sulit.
Secara berkala
Jalankan adaptor sinkronisasi setelah interval yang Anda pilih berakhir, atau jalankan pada interval tertentu setiap hari.
On demand
Jalankan adaptor sinkronisasi sebagai respons terhadap tindakan pengguna. Namun, untuk menyediakan pengguna terbaik pengalaman yang lebih baik, Anda harus mengandalkan terutama salah satu opsi yang lebih otomatis. Dengan menggunakan otomatis, Anda menghemat baterai dan sumber daya jaringan.

Selanjutnya, tutorial ini menjelaskan setiap opsi secara lebih detail.

Menjalankan adaptor sinkronisasi saat data server berubah

Jika aplikasi Anda mentransfer data dari server dan data server sering berubah, Anda bisa menggunakan {i>sync<i} adaptor untuk melakukan {i>download<i} sebagai respons terhadap perubahan data. Untuk menjalankan adaptor sinkronisasi, minta server mengirim pesan khusus ke BroadcastReceiver di aplikasi Anda. Sebagai respons atas pesan ini, panggil ContentResolver.requestSync() untuk memberi sinyal pada framework adaptor sinkronisasi untuk menjalankan {i>synchronize<i}.

Google Cloud Messaging (GCM) menyediakan server dan perangkat yang Anda butuhkan agar sistem pesan ini berfungsi. Menggunakan GCM untuk memicu lebih andal dan lebih efisien daripada server polling untuk mengetahui status. Saat melakukan polling memerlukan Service yang selalu aktif, GCM menggunakan BroadcastReceiver yang diaktifkan saat pesan masuk. Saat melakukan polling secara berkala menggunakan daya baterai bahkan jika tidak ada pembaruan yang tersedia, GCM hanya mengirim dalam pesan teks saat diperlukan.

Catatan: Jika Anda menggunakan GCM untuk memicu adaptor sinkronisasi melalui siaran ke semua di perangkat tempat aplikasi Anda diinstal. Ingatlah bahwa aplikasi tersebut menerima pesan Anda di di waktu yang hampir sama. Situasi ini dapat menyebabkan beberapa instance adaptor sinkronisasi berjalan pada saat yang sama, menyebabkan kelebihan beban server dan jaringan. Untuk menghindari situasi ini pada siaran ke semua perangkat, Anda harus mempertimbangkan untuk menunda waktu dimulainya adaptor sinkronisasi selama yang unik untuk setiap perangkat.

Cuplikan kode berikut menunjukkan cara menjalankan requestSync() sebagai respons terhadap pesan GCM masuk:

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

Menjalankan adaptor sinkronisasi saat data penyedia konten berubah

Jika aplikasi Anda mengumpulkan data di penyedia konten, dan Anda ingin memperbarui server setiap kali Anda memperbarui penyedia, Anda bisa mengatur aplikasi agar menjalankan adaptor sinkronisasi Anda secara otomatis. Yang akan dilakukan ini, Anda mendaftarkan observer untuk penyedia konten. Kapan data di penyedia konten Anda perubahan, kerangka kerja penyedia konten memanggil pengamat. Dalam observer, panggil requestSync() untuk memberi tahu framework agar berjalan adaptor sinkronisasi Anda.

Catatan: Jika Anda menggunakan penyedia konten stub, Anda tidak memiliki data apa pun di penyedia konten dan onChange() tidak pernah dipanggil. Dalam hal ini, Anda harus menyediakan mekanisme Anda sendiri untuk mendeteksi perubahan pada data perangkat. Mekanisme ini juga bertanggung jawab untuk memanggil requestSync() saat data berubah.

Untuk membuat observer bagi penyedia konten Anda, perluas class ContentObserver dan menerapkan kedua bentuk Metode onChange(). Di beberapa onChange(), telepon requestSync() untuk memulai adaptor sinkronisasi.

Untuk mendaftarkan observer, teruskan sebagai argumen dalam panggilan ke registerContentObserver(). Di beberapa Anda juga harus meneruskan URI konten untuk data yang ingin Anda amati. Konten framework penyedia membandingkan URI smartwatch ini dengan URI konten yang diteruskan sebagai argumen Metode ContentResolver yang mengubah penyedia Anda, seperti ContentResolver.insert(). Jika ada kecocokan, implementasi ContentObserver.onChange() dipanggil.

Cuplikan kode berikut menunjukkan cara menentukan ContentObserver yang memanggil requestSync() saat tabel perubahan:

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

Menjalankan adaptor sinkronisasi secara berkala

Anda bisa menjalankan adaptor sinkronisasi secara berkala dengan mengatur jangka waktu tunggu antarproses, atau dengan menjalankannya pada waktu tertentu dalam sehari, atau keduanya. Menjalankan adaptor sinkronisasi secara berkala memungkinkan Anda untuk kira-kira cocok dengan interval pembaruan server Anda.

Demikian pula, Anda bisa mengunggah data dari perangkat saat server relatif tidak ada aktivitas, dengan menjadwalkan adaptor sinkronisasi untuk dijalankan di malam hari. Sebagian besar pengguna membiarkan perangkat tetap menyala dan terhubung pada malam hari, jadi waktu ini biasanya tersedia. Selain itu, perangkat tidak menjalankan tugas lain di waktu yang sama dengan adaptor sinkronisasi Anda. Namun, jika Anda mengambil pendekatan ini, Anda perlu memastikan bahwa setiap perangkat memicu transfer data pada waktu yang sedikit berbeda. Jika semua perangkat menjalankan adaptor sinkronisasi di saat yang sama, kemungkinan besar Anda akan membebani server dan data penyedia seluler jaringan.

Secara umum, operasi berkala masuk akal jika pengguna Anda tidak memerlukan pembaruan instan, tetapi mengharapkan memiliki pembaruan rutin. Lari berkala juga masuk akal jika Anda ingin menyeimbangkan ketersediaan data terbaru dengan efisiensi operasi adaptor sinkronisasi yang lebih kecil dan tidak menggunakan perangkat secara berlebihan Google Cloud Platform.

Untuk menjalankan adaptor sinkronisasi secara berkala, panggil addPeriodicSync(). Hal ini menjadwalkan {i>sync<i} untuk dijalankan setelah jangka waktu tertentu telah berlalu. Karena framework adaptor sinkronisasi harus memperhitungkan eksekusi adaptor sinkronisasi lain dan mencoba memaksimalkan efisiensi baterai, waktu berlalu dapat berbeda beberapa detik. Selain itu, kerangka kerja tidak akan menjalankan adaptor sinkronisasi Anda jika jaringan tidak tersedia.

Perhatikan bahwa addPeriodicSync() tidak menjalankan adaptor sinkronisasi pada waktu tertentu. Untuk menjalankan adaptor sinkronisasi di sekitar pada waktu yang sama setiap hari, gunakan alarm berulang sebagai pemicu. Alarm berulang dijelaskan lebih lanjut di dokumentasi referensi untuk AlarmManager. Jika Anda menggunakan metode setInexactRepeating() untuk menyetel yang memiliki beberapa variasi, Anda tetap harus mengacak waktu mulai untuk memastikan bahwa adaptor sinkronisasi yang berjalan dari perangkat berbeda secara bertahap.

Metode addPeriodicSync() tidak nonaktifkan setSyncAutomatically(), sehingga Anda mungkin mendapatkan beberapa sinkronisasi yang berjalan dalam waktu yang relatif singkat. Selain itu, hanya beberapa penanda kontrol adaptor sinkronisasi diizinkan dalam panggilan ke addPeriodicSync(); penanda yang tidak diizinkan dijelaskan dalam dokumentasi yang dirujuk untuk addPeriodicSync().

Cuplikan kode berikut menunjukkan cara menjadwalkan pengoperasian adaptor sinkronisasi berkala:

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

Menjalankan adaptor sinkronisasi berdasarkan permintaan

Menjalankan adaptor sinkronisasi sebagai respons terhadap permintaan pengguna adalah strategi yang paling tidak disarankan untuk menjalankan adaptor sinkronisasi. Kerangka kerja ini dirancang khusus untuk menghemat daya baterai saat menjalankan adaptor sinkronisasi sesuai dengan jadwal. Opsi yang menjalankan sinkronisasi sebagai respons terhadap data perubahan menggunakan daya baterai secara efektif, karena daya itu digunakan untuk menyediakan data baru.

Sebagai perbandingan, mengizinkan pengguna untuk menjalankan sinkronisasi sesuai permintaan berarti bahwa sinkronisasi berjalan dengan sendirinya, yang adalah penggunaan sumber daya jaringan dan daya yang tidak efisien. Selain itu, menyediakan sinkronisasi on demand membuat pengguna meminta sinkronisasi meskipun tidak ada bukti bahwa data tersebut telah berubah, dan menjalankan sinkronisasi yang tidak memperbarui data adalah penggunaan daya baterai yang tidak efektif. Secara umum, aplikasi Anda harus menggunakan sinyal lain untuk memicu sinkronisasi atau menjadwalkannya secara berkala, tanpa input pengguna.

Namun, jika Anda masih ingin menjalankan adaptor sinkronisasi sesuai permintaan, atur flag adaptor sinkronisasi untuk jalankan adaptor sinkronisasi manual, lalu panggil ContentResolver.requestSync().

Jalankan transfer berdasarkan permintaan dengan tanda berikut:

SYNC_EXTRAS_MANUAL
Memaksa sinkronisasi manual. Kerangka kerja adaptor sinkronisasi mengabaikan pengaturan yang ada, seperti flag yang ditetapkan oleh setSyncAutomatically().
SYNC_EXTRAS_EXPEDITED
Memaksa sinkronisasi untuk segera dimulai. Jika Anda tidak mengaturnya, sistem mungkin akan menunggu beberapa detik sebelum menjalankan permintaan sinkronisasi, karena mencoba mengoptimalkan penggunaan baterai dengan menjadwalkan banyak permintaan dalam waktu singkat.

Cuplikan kode berikut menunjukkan cara memanggil requestSync() sebagai respons terhadap tombol klik:

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