Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Menjalankan adaptor sinkronisasi

Catatan: Kami merekomendasikan WorkManager sebagai solusi untuk sebagian besar kasus penggunaan pemrosesan di latar belakang. Harap baca panduan pemrosesan di latar belakang untuk mengetahui solusi terbaik bagi Anda.

Di tutorial sebelumnya pada kelas ini, Anda telah mempelajari cara membuat komponen adaptor sinkronisasi yang merangkum kode transfer data, serta cara menambahkan komponen tambahan yang memungkinkan Anda menyambungkan adaptor sinkronisasi ke dalam sistem. Kini Anda memiliki segala yang diperlukan 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 beberapa peristiwa. Misalnya, Anda mungkin ingin adaptor sinkronisasi berjalan dalam jadwal rutin, baik setelah jangka waktu tertentu atau pada waktu tertentu. Anda mungkin juga ingin menjalankan adaptor sinkronisasi saat ada perubahan pada data yang disimpan di perangkat. Sebaiknya hindari menjalankan adaptor sinkronisasi sebagai hasil langsung dari tindakan pengguna, karena Anda tidak akan memperoleh manfaat penuh dari kemampuan penjadwalan framework adaptor sinkronisasi jika melakukan tindakan tersebut. Misalnya, sebaiknya hindari menyediakan tombol refresh pada antarmuka pengguna.

Anda memiliki opsi berikut untuk menjalankan adaptor sinkronisasi:

Saat data server berubah
Jalankan adaptor sinkronisasi sebagai respons atas pesan dari server, yang menunjukkan bahwa data berbasis server telah berubah. Opsi ini memungkinkan Anda untuk memuat ulang data dari server ke perangkat tanpa menurunkan performa atau menghabiskan 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 data yang dimodifikasi dari perangkat ke server, dan sangat berguna jika Anda perlu memastikan agar server selalu memiliki data perangkat terbaru. Opsi ini mudah diterapkan jika Anda benar-benar menyimpan data di penyedia konten. Jika Anda menggunakan penyedia konten stub, deteksi perubahan data mungkin akan lebih sulit.
Secara berkala
Jalankan adaptor sinkronisasi setelah interval yang Anda pilih berakhir, atau jalankan di waktu tertentu setiap hari.
Sesuai permintaan
Jalankan adaptor sinkronisasi sebagai respons terhadap tindakan pengguna. Namun, untuk memberikan pengalaman pengguna terbaik, Anda harus mengandalkan, terutama, salah satu opsi yang lebih otomatis. Dengan opsi otomatis, Anda akan menghemat sumber daya baterai dan 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 dapat menggunakan adaptor sinkronisasi untuk melakukan download sebagai respons terhadap perubahan data. Untuk menjalankan adaptor sinkronisasi, minta server mengirimkan pesan khusus ke BroadcastReceiver di aplikasi Anda. Sebagai respons terhadap pesan ini, panggil ContentResolver.requestSync() untuk memberi sinyal pada framework adaptor sinkronisasi untuk menjalankan adaptor sinkronisasi Anda.

Google Cloud Messaging (GCM) menyediakan komponen server dan perangkat yang Anda perlukan agar sistem messaging ini berfungsi. Penggunaan GCM untuk memicu transfer lebih dapat diandalkan dan lebih efisien daripada melakukan polling pada server untuk status. Polling memerlukan Service yang selalu aktif, sedangkan GCM menggunakan BroadcastReceiver yang diaktifkan saat pesan masuk. Meskipun polling secara berkala menggunakan daya baterai walau tidak ada update, GCM hanya mengirim pesan bila diperlukan.

Catatan: Jika Anda menggunakan GCM untuk memicu adaptor sinkronisasi melalui siaran ke semua perangkat tempat aplikasi diinstal, perlu diingat bahwa pesan Anda akan diterima di saat yang hampir bersamaan. Situasi ini dapat menyebabkan beberapa instance adaptor sinkronisasi berjalan di waktu yang sama, sehingga menyebabkan server dan jaringan kelebihan beban. Agar tidak terjadi siaran ke semua perangkat, Anda harus mempertimbangkan penundaan dimulainya adaptor sinkronisasi selama periode yang unik untuk setiap perangkat.

Cuplikan kode berikut menunjukkan cara menjalankan requestSync() sebagai respons terhadap pesan GCM yang 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 mengumpulkan data di penyedia konten, dan Anda ingin mengupdate server setiap kali mengupdate penyedia, Anda dapat menyiapkan aplikasi agar menjalankan adaptor sinkronisasi secara otomatis. Untuk melakukannya, Anda perlu mendaftarkan observer untuk penyedia konten. Jika data di penyedia konten Anda berubah, framework penyedia konten akan memanggil pengamat. Dalam pengamat tersebut, panggil requestSync() untuk memberi tahu framework agar menjalankan adaptor sinkronisasi.

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

Untuk membuat suatu pengamat bagi penyedia konten Anda, perluas kelas ContentObserver dan terapkan kedua bentuk metode onChange(). Di onChange(), panggil requestSync() untuk memulai adaptor sinkronisasi.

Untuk mendaftarkan pengamat, teruskan sebagai argumen dalam panggilan ke registerContentObserver(). Dalam panggilan ini, Anda juga harus meneruskan URI konten untuk data yang ingin diamati. Framework penyedia konten akan membandingkan URI pantauan ini dengan URI konten yang diteruskan sebagai argumen untuk metode ContentResolver yang mengubah penyedia Anda, seperti ContentResolver.insert(). Jika ada kecocokan, penerapan ContentObserver.onChange() akan dipanggil.

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

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 dapat menjalankan adaptor sinkronisasi secara berkala dengan menyetel jangka waktu tunggu antarproses, atau dengan menjalankannya pada waktu tertentu, atau bahkan keduanya. Menjalankan adaptor sinkronisasi secara berkala memungkinkan Anda mencocokkan interval update server secara kasar.

Demikian pula, Anda dapat mengupload data dari perangkat saat server relatif tidak aktif, dengan menjadwalkan adaptor sinkronisasi agar berjalan di malam hari. Sebagian besar pengguna membiarkannya menyala dan tersambung di malam hari, sehingga pada saat ini biasanya server tersedia. Selain itu, perangkat tidak sedang menjalankan tugas lain secara bersamaan dengan adaptor sinkronisasi Anda. Akan tetapi, jika melakukan pendekatan ini, Anda harus memastikan bahwa setiap perangkat memicu transfer data di waktu yang sedikit berbeda. Jika semua perangkat menjalankan adaptor sinkronisasi secara bersamaan, Anda akan cenderung membebani server dan jaringan data penyedia seluler Anda.

Secara umum, menjalankan proses ini secara berkala merupakan hal yang masuk akal, jika pengguna Anda tidak memerlukan update instan, namun ingin mendapatkan update rutin. Menjalankan proses secara berkala juga masuk akal jika Anda ingin menyeimbangkan ketersediaan data terbaru dengan efisiensi proses sinkronisasi adaptor yang lebih kecil, yang tidak menggunakan resource perangkat secara berlebihan.

Untuk menjalankan adaptor sinkronisasi secara berkala, panggil addPeriodicSync(). Tindakan tersebut akan menjadwalkan adaptor sinkronisasi Anda agar berjalan setelah berlalunya jangka waktu tertentu. Karena framework adaptor sinkronisasi harus memperhitungkan eksekusi adaptor lain dan mencoba memaksimalkan efisiensi baterai, waktu berlalu dapat bervariasi selama beberapa detik. Selain itu, framework tidak akan menjalankan adaptor sinkronisasi jika jaringan tidak tersedia.

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

Metode addPeriodicSync() tidak menonaktifkan setSyncAutomatically(), sehingga Anda bisa mendapatkan beberapa sinkronisasi yang berjalan dalam waktu yang relatif singkat. Selain itu, hanya beberapa flag kontrol adaptor sinkronisasi yang diperbolehkan dalam panggilan ke addPeriodicSync(); penjelasan untuk flag yang tidak diizinkan dapat ditemukan dalam dokumentasi referensi 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 atas permintaan pengguna adalah strategi yang paling tidak disarankan untuk menjalankan adaptor sinkronisasi. Framework ini didesain khusus untuk menghemat daya baterai saat menjalankan adaptor sinkronisasi sesuai jadwal. Opsi yang menjalankan sinkronisasi sebagai respons terhadap perubahan data menggunakan daya baterai secara efektif, karena daya tersebut digunakan untuk menyediakan data baru.

Sebagai perbandingan, mengizinkan pengguna untuk menjalankan sinkronisasi berdasarkan permintaan berarti bahwa sinkronisasi berjalan dengan sendirinya, sehingga penggunaan resource jaringan dan daya menjadi tidak efisien. Selain itu, menyediakan sinkronisasi berdasarkan permintaan akan membuat pengguna meminta sinkronisasi meskipun tidak ada bukti bahwa data berubah, dan menjalankan sinkronisasi tanpa me-refresh data adalah tindakan yang memboroskan baterai. Secara umum, aplikasi Anda harus menggunakan sinyal lain untuk memicu sinkronisasi atau menjadwalkannya secara berkala, tanpa masukan dari pengguna.

Namun, jika Anda tetap ingin menjalankan adaptor sinkronisasi berdasarkan permintaan, setel flag adaptor sinkronisasi agar menjalankan adaptor sinkronisasi secara manual, kemudian panggil ContentResolver.requestSync().

Jalankan transfer berdasarkan permintaan dengan flag berikut:

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

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

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