Not: Arka planda işleme özelliklerinin çoğu için önerilen çözüm olarak WorkManager'ı öneriyoruz. Size en uygun çözümü öğrenmek için lütfen arka planda işleme kılavuzuna göz atın.
Bu sınıfın önceki derslerinde, veri aktarım kodunu içeren bir senkronizasyon adaptörü bileşeninin nasıl oluşturulacağını ve senkronizasyon adaptörünü sisteme bağlamanızı sağlayan ek bileşenlerin nasıl ekleneceğini öğrendiniz. Artık senkronizasyon bağdaştırıcısı içeren bir uygulama yüklemek için ihtiyacınız olan her şeye sahipsiniz, ancak gördüğünüz kodların hiçbiri aslında senkronizasyon bağdaştırıcısını çalıştırmıyor.
Senkronizasyon bağdaştırıcınızı bir programa göre veya bir etkinliğin dolaylı sonucu olarak çalıştırmayı denemelisiniz. Örneğin, senkronizasyon bağdaştırıcınızın belirli bir süre sonra veya günün belirli bir saatinde düzenli bir şekilde çalışmasını isteyebilirsiniz. Cihazda depolanan verilerde değişiklikler olduğunda da senkronizasyon adaptörünüzü çalıştırmak isteyebilirsiniz. Senkronizasyon bağdaştırıcınızı bir kullanıcı işleminin doğrudan sonucu olarak çalıştırmaktan kaçınmalısınız. Bunu yaptığınızda senkronizasyon bağdaştırıcısı çerçevesinin planlama özelliğinden tam olarak yararlanamazsınız. Örneğin, kullanıcı arayüzünde yenileme düğmesi eklemekten kaçınmalısınız.
Senkronizasyon bağdaştırıcınızı çalıştırmak için aşağıdaki seçenekleri kullanabilirsiniz:
- Sunucu verileri değiştiğinde
- Sunucu tabanlı verilerin değiştiğini belirten bir mesaja yanıt olarak senkronizasyon bağdaştırıcısını çalıştırın. Bu seçenek, performansı düşürmeden veya sunucuyu yoklayarak pil ömrünü harcamadan sunucudan cihaza verileri yenilemenize olanak tanır.
- Cihaz verileri değiştiğinde
- Cihazdaki veriler değiştiğinde bir senkronizasyon adaptörü çalıştırın. Bu seçenek, değiştirilmiş verileri cihazdan bir sunucuya göndermenize olanak tanır ve özellikle sunucunun her zaman en güncel cihaz verilerine sahip olmasını sağlamanız gerektiğinde yararlı olur. İçerik sağlayıcınızda gerçekten veri depoluyorsanız bu seçeneğin uygulanması oldukça kolaydır. Bir saplama içeriği sağlayıcısı kullanıyorsanız veri değişikliklerini algılamak daha zor olabilir.
- Düzenli aralıklarla
- Seçtiğiniz aralığın süresi dolduktan sonra senkronizasyon bağdaştırıcısını çalıştırın veya her gün belirli bir zamanda çalıştırın.
- İstek üzerine
- Kullanıcı işlemine yanıt olarak senkronizasyon bağdaştırıcısını çalıştırın. Ancak en iyi kullanıcı deneyimini sağlamak için öncelikli olarak daha otomatik seçeneklerden birini kullanmalısınız. Otomatik seçenekleri kullanarak pil ve ağ kaynaklarından tasarruf edersiniz.
Bu dersin geri kalanında, bu seçenekler her biri ayrıntılı olarak açıklanmaktadır.
Sunucu verileri değiştiğinde senkronizasyon bağdaştırıcısını çalıştırma
Uygulamanız bir sunucudan veri aktarıyorsa ve sunucu verileri sık sık değişiyorsa veri değişikliklerine yanıt olarak indirme işlemi yapmak için bir senkronizasyon bağdaştırıcısı kullanabilirsiniz. Senkronizasyon bağdaştırıcısını çalıştırmak için sunucunun uygulamanızdaki bir BroadcastReceiver
öğesine özel bir mesaj göndermesini sağlayın.
Bu mesaja yanıt olarak, senkronizasyon bağdaştırıcısı çerçevesine senkronizasyon bağdaştırıcınızı çalıştırmak üzere sinyal vermek için ContentResolver.requestSync()
çağrısı yapın.
Google Cloud Messaging (GCM), bu mesajlaşma sisteminin çalışması için ihtiyacınız olan hem sunucu hem de cihaz bileşenlerini sağlar. Aktarımları tetiklemek için GCM'yi kullanmak, durum için yoklama sunucularına göre daha güvenilir ve daha verimlidir. Yoklama işlemi için her zaman etkin olan bir Service
gerekirken GCM tarafından, ileti geldiğinde etkinleştirilen bir BroadcastReceiver
kullanılır. Düzenli aralıklarla yoklama yaparken, herhangi bir güncelleme olmasa bile pil gücünü kullanır. GCM tarafından yalnızca gerektiğinde mesaj gönderilir.
Not: Uygulamanızın yüklü olduğu tüm cihazlara yapılan bir yayın aracılığıyla senkronizasyon bağdaştırıcınızı tetiklemek için GCM'yi kullanıyorsanız mesajınızın yaklaşık olarak aynı zamanda aldıklarını unutmayın. Bu durum, senkronizasyon bağdaştırıcınızın birden fazla örneğinin aynı anda çalışmasına ve dolayısıyla sunucu ve ağda aşırı yük oluşmasına neden olabilir. Tüm cihazlara yayın yapıldığında bu durumu önlemek için senkronizasyon bağdaştırıcısının başlangıcını her cihazda benzersiz bir süreye ertelemeyi düşünmeniz gerekir.
Aşağıdaki kod snippet'i, gelen bir GCM mesajına yanıt olarak requestSync()
hizmetinin nasıl çalıştırılacağını gösterir:
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); ... } ... } ... }
İçerik sağlayıcı verileri değiştiğinde senkronizasyon adaptörünü çalıştır
Uygulamanız bir içerik sağlayıcıda veri topluyorsa ve sağlayıcıyı her güncellediğinizde sunucuyu güncellemek istiyorsanız uygulamanızı senkronizasyon bağdaştırıcınızı otomatik olarak çalıştıracak şekilde ayarlayabilirsiniz. Bunu yapmak için içerik sağlayıcı için bir gözlemci kaydedersiniz. İçerik sağlayıcınızdaki veriler değiştiğinde, içerik sağlayıcı çerçevesi gözlemciyi çağırır. Gözlemcide, çerçeveye senkronizasyon bağdaştırıcınızı çalıştırmasını bildirmek için requestSync()
çağrısı yapın.
Not: Kaba içerik sağlayıcı kullanıyorsanız içerik sağlayıcıda hiç veriniz olmaz ve onChange()
hiçbir zaman çağrılmaz. Bu durumda, cihaz verilerindeki değişiklikleri algılamak için kendi mekanizmanızı sağlamanız gerekir. Bu mekanizma, veriler değiştiğinde requestSync()
çağrısından da sorumludur.
İçerik sağlayıcınız için bir gözlemci oluşturmak istiyorsanız ContentObserver
sınıfını genişletin ve onChange()
yönteminin her iki biçimini de uygulayın. onChange()
ürününde, senkronizasyon bağdaştırıcısını başlatmak için requestSync()
çağrısı yapın.
Gözlemciyi kaydetmek için registerContentObserver()
çağrısında bağımsız değişken olarak iletin. Bu çağrıda, izlemek istediğiniz veriler için bir içerik URI'si iletmeniz de gerekir. İçerik sağlayıcı çerçevesi, bu izleme URI'sini sağlayıcınızı değiştiren ContentResolver
yöntemlerine (ör. ContentResolver.insert()
) bağımsız değişken olarak aktarılan içerik URI'leriyle karşılaştırır. Eşleşme varsa ContentObserver.onChange()
uygulamanız çağrılır.
Aşağıdaki kod snippet'i, bir tablo değiştiğinde requestSync()
çağrısı yapan bir ContentObserver
öğesini nasıl tanımlayacağınızı gösterir:
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); ... } ... }
Senkronizasyon bağdaştırıcısını düzenli aralıklarla çalıştırma
Senkronizasyon bağdaştırıcınızı çalıştırmalar arasında beklenecek bir süre ayarlayarak, günün belirli saatlerinde çalıştırarak ya da her ikisini birden yaparak düzenli aralıklarla çalıştırabilirsiniz. Senkronizasyon bağdaştırıcınızı düzenli aralıklarla çalıştırmak, sunucunuzun güncelleme aralığıyla yaklaşık olarak eşleşmenize olanak tanır.
Benzer şekilde, senkronizasyon bağdaştırıcınızı gece çalışacak şekilde planlayarak sunucunuz nispeten boştayken cihazdan veri yükleyebilirsiniz. Çoğu kullanıcı geceleri güçlerini açık ve fişe takılı bıraktığından bu saat genellikle uygun olur. Ayrıca cihaz, senkronizasyon adaptörünüzle aynı anda diğer görevleri çalıştırmıyordur. Ancak bu yaklaşımı benimserseniz her cihazın veri aktarımını biraz farklı bir zamanda tetiklediğinden emin olmanız gerekir. Tüm cihazlar senkronizasyon bağdaştırıcınızı aynı anda çalıştırıyorsa sunucu ve hücresel sağlayıcı veri ağlarınızda aşırı yüklenmeye yol açmış olabilirsiniz.
Periyodik çalıştırmalar genel olarak kullanıcılarınızın anlık güncellemelere ihtiyaç duymayıp düzenli olarak güncelleme yapmasını bekliyorsa anlamlıdır. Periyodik çalıştırmalar, güncel verilerin kullanılabilirliğini, cihaz kaynaklarını aşırı kullanmayan küçük senkronizasyon bağdaştırıcı çalıştırmalarının verimliliğiyle dengelemek istediğinizde de mantıklıdır.
Senkronizasyon bağdaştırıcınızı düzenli aralıklarla çalıştırmak için addPeriodicSync()
numaralı telefonu arayın. Bu işlem, senkronizasyon bağdaştırıcınızı belirli bir süre sonra çalışacak şekilde programlar. Senkronizasyon adaptörü çerçevesinin diğer senkronizasyon adaptörü yürütmelerini de hesaba katması ve pil verimliliğini en üst düzeye çıkarmaya çalışması gerektiğinden, geçen süre birkaç saniye farklılık gösterebilir. Ayrıca ağ yoksa çerçeve, senkronizasyon bağdaştırıcınızı çalıştırmaz.
addPeriodicSync()
politikasının, senkronizasyon bağdaştırıcısını günün belirli bir saatinde çalıştırmadığına dikkat edin. Senkronizasyon adaptörünüzü her gün yaklaşık olarak aynı saatte çalıştırmak için tetikleyici olarak yinelenen bir alarm kullanın. Tekrarlanan alarmlar, AlarmManager
referans dokümanlarında daha ayrıntılı açıklanmıştır. Bazı farklılıklara sahip günün saati tetikleyicileri ayarlamak için setInexactRepeating()
yöntemini kullanıyorsanız senkronizasyon bağdaştırıcısının farklı cihazlardan çalıştığından emin olmak için yine de başlangıç zamanını rastgele hale getirmeniz gerekir.
addPeriodicSync()
yöntemi setSyncAutomatically()
öğesini devre dışı bırakmaz. Bu nedenle, nispeten kısa bir süre içinde birden fazla senkronizasyon işlemi alabilirsiniz. Ayrıca addPeriodicSync()
çağrısında yalnızca birkaç senkronizasyon bağdaştırıcısı kontrol bayrağına izin verilir. İzin verilmeyen işaretler addPeriodicSync()
için başvurulan dokümanlarda açıklanmaktadır.
Aşağıdaki kod snippet'inde, periyodik senkronizasyon bağdaştırıcısı çalıştırmalarının nasıl planlanacağı gösterilmektedir:
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); ... } ... }
Senkronizasyon bağdaştırıcısını isteğe bağlı olarak çalıştırma
Senkronizasyon bağdaştırıcınızı bir kullanıcı isteğine yanıt olarak çalıştırmak, senkronizasyon bağdaştırıcısını çalıştırmak için en az tercih edilen stratejidir. Çerçeve, senkronizasyon adaptörlerini bir programa göre çalıştırırken pil gücünden tasarruf etmek için özel olarak tasarlanmıştır. Veri değişikliklerine yanıt olarak senkronizasyon gerçekleştiren seçenekler, yeni verileri sağlamak için güç kullanıldığından, pil gücünü verimli bir şekilde kullanır.
Buna karşılık, kullanıcıların istek üzerine senkronizasyon çalıştırabilmesi, senkronizasyonun kendi başına çalışması anlamına gelir. Bu da ağ ve güç kaynaklarının verimsiz bir şekilde kullanılmasına neden olur. Ayrıca, istek üzerine senkronizasyon sağlanması, verilerin değiştiğine dair bir kanıt olmasa bile kullanıcıların senkronizasyon isteğinde bulunmasına neden olur. Verileri yenilemeyen bir senkronizasyon çalıştırmak, pil gücünün etkisiz bir kullanımıdır. Genel olarak uygulamanız, senkronizasyonu tetiklemek için diğer sinyalleri kullanmalıdır ya da kullanıcı girişi olmadan düzenli aralıklarla planlamalıdır.
Ancak senkronizasyon bağdaştırıcısını isteğe bağlı olarak çalıştırmaya devam etmek istiyorsanız manuel senkronizasyon bağdaştırıcısı bayraklarını ayarlayıp ContentResolver.requestSync()
yöntemini çağırın.
Aşağıdaki işaretlerle isteğe bağlı aktarımlar çalıştırın:
-
SYNC_EXTRAS_MANUAL
-
Manuel senkronizasyonu zorunlu kılar. Senkronizasyon bağdaştırıcısı çerçevesi,
setSyncAutomatically()
tarafından ayarlanan işaret gibi mevcut ayarları yoksayar. -
SYNC_EXTRAS_EXPEDITED
- Senkronizasyonun hemen başlatılmasını sağlar. Bu seçeneği ayarlamazsanız kısa bir süre içinde çok sayıda istek planlayarak pil kullanımını optimize etmeye çalıştığından sistem, senkronizasyon isteğini çalıştırmadan önce birkaç saniye bekleyebilir.
Aşağıdaki kod snippet'i, düğme tıklamasına yanıt olarak requestSync()
çağrısının nasıl yapılacağını gösterir:
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); }