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