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 di class ini, Anda telah mempelajari cara membuat komponen adaptor sinkronisasi yang merangkum kode transfer data, dan cara menambahkan komponen tambahan yang memungkinkan Anda mencolokkan adaptor sinkronisasi ke sistem. Kini Anda memiliki semua 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 sesuai 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 dengan melakukan hal ini, Anda tidak akan mendapatkan manfaat penuh dari kemampuan penjadwalan framework adaptor sinkronisasi. 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 terhadap pesan dari server, yang menunjukkan bahwa data berbasis server telah berubah. Opsi ini memungkinkan Anda 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 bahwa server selalu memiliki data perangkat terbaru. Opsi ini mudah diterapkan jika Anda benar-benar menyimpan data di penyedia konten Anda. 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.
- On demand
- Jalankan adaptor sinkronisasi sebagai respons terhadap tindakan pengguna. Namun, untuk memberikan pengalaman pengguna terbaik, Anda harus terutama mengandalkan salah satu opsi yang lebih otomatis. Dengan menggunakan opsi otomatis, Anda akan menghemat resource jaringan dan baterai.
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 mengirim pesan khusus ke BroadcastReceiver
di aplikasi Anda.
Sebagai respons terhadap pesan ini, panggil ContentResolver.requestSync()
untuk memberi sinyal pada framework adaptor sinkronisasi agar menjalankan
adaptor sinkronisasi Anda.
Google Cloud Messaging (GCM) menyediakan komponen server dan perangkat yang Anda perlukan agar sistem pesan ini berfungsi. Menggunakan GCM untuk memicu transfer lebih andal dan lebih efisien daripada melakukan polling 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 meskipun tidak ada pembaruan, GCM hanya mengirim
pesan jika 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 perangkat tersebut di saat yang hampir bersamaan. Situasi ini dapat menyebabkan beberapa instance adaptor sinkronisasi berjalan secara bersamaan, sehingga menyebabkan server dan jaringan kelebihan beban. Agar tidak terjadi siaran ke semua perangkat, sebaiknya pertimbangkan untuk menunda 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 Anda 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, daftarkan observer untuk penyedia konten. Jika data di penyedia konten Anda
berubah, framework penyedia konten akan memanggil observer. Dalam observer, panggil requestSync()
untuk memberi tahu framework agar menjalankan adaptor sinkronisasi Anda.
Catatan: Jika menggunakan penyedia konten stub, Anda tidak memiliki data di penyedia konten dan onChange()
tidak pernah dipanggil. Dalam hal ini, Anda harus menyediakan mekanisme 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 implementasikan kedua bentuk
metode onChange()
. Di onChange()
, panggil requestSync()
untuk memulai adaptor sinkronisasi.
Untuk mendaftarkan observer, teruskan sebagai argumen dalam panggilan ke
registerContentObserver()
. Dalam
panggilan ini, Anda juga harus meneruskan URI konten untuk data yang ingin dipantau. Framework
penyedia konten akan membandingkan URI tontonan ini dengan URI konten yang diteruskan sebagai argumen ke
metode ContentResolver
yang mengubah penyedia Anda, seperti
ContentResolver.insert()
. Jika ada kecocokan, penerapan ContentObserver.onChange()
akan dipanggil.
Cuplikan kode berikut menunjukkan cara menetapkan 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 periode waktu tunggu antar-proses, atau dengan menjalankannya pada waktu tertentu, atau 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 ada aktivitas, dengan menjadwalkan adaptor sinkronisasi agar berjalan di malam hari. Sebagian besar pengguna membiarkannya menyala dan dicolokkan di malam hari, sehingga waktu ini biasanya tersedia. Selain itu, perangkat tidak sedang menjalankan tugas lain secara bersamaan dengan adaptor sinkronisasi Anda. Namun, jika melakukan pendekatan ini, Anda harus memastikan bahwa setiap perangkat memicu transfer data pada waktu yang sedikit berbeda. Jika semua perangkat menjalankan adaptor sinkronisasi Anda secara bersamaan, kemungkinan Anda akan membebani jaringan data server dan penyedia seluler.
Secara umum, menjalankan proses secara berkala wajar jika pengguna tidak memerlukan update instan, tetapi ingin mendapatkan update rutin. Menjalankan secara berkala juga masuk akal jika Anda ingin menyeimbangkan ketersediaan data terbaru dengan efisiensi operasi adaptor sinkronisasi yang lebih kecil yang tidak menggunakan resource perangkat secara berlebihan.
Untuk menjalankan adaptor sinkronisasi secara berkala, panggil addPeriodicSync()
. Tindakan ini akan menjadwalkan adaptor sinkronisasi untuk berjalan setelah jangka waktu tertentu berlalu. Karena framework adaptor sinkronisasi harus memperhitungkan eksekusi adaptor sinkronisasi lainnya 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
mendetail dalam dokumentasi referensi untuk AlarmManager
. Jika Anda menggunakan metode setInexactRepeating()
untuk menetapkan pemicu waktu yang memiliki beberapa variasi, Anda tetap harus mengacak waktu mulai untuk memastikan bahwa 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 diizinkan dalam panggilan ke
addPeriodicSync()
; flag yang
tidak diizinkan dijelaskan 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 terhadap permintaan pengguna adalah strategi yang paling tidak disarankan untuk menjalankan adaptor sinkronisasi. Framework ini dirancang 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 menjalankan sinkronisasi sesuai permintaan berarti sinkronisasi berjalan dengan sendirinya, sehingga penggunaan resource jaringan dan daya menjadi tidak efisien. Selain itu, menyediakan sinkronisasi sesuai permintaan akan membuat pengguna meminta sinkronisasi meskipun tidak ada bukti bahwa data telah berubah, dan menjalankan sinkronisasi yang tidak memuat ulang 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 tetap ingin menjalankan adaptor sinkronisasi sesuai permintaan, setel tanda adaptor sinkronisasi agar adaptor sinkronisasi berjalan secara manual, lalu panggil ContentResolver.requestSync()
.
Jalankan transfer berdasarkan permintaan dengan tanda 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 dapat 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 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); }