Arka plan optimizasyonu

Arka plan işlemleri, bellek ve pil açısından yoğun olabilir. Örneğin, örtülü bir yayın, çok fazla iş yapmayacak olsalar bile dinlemek için kaydolan birçok arka plan işlemini başlatabilir. Bu durum, hem cihaz performansı hem de kullanıcı deneyimi üzerinde önemli bir etkiye sahip olabilir.

Bu sorunu hafifletmek için Android 7.0 (API düzeyi 24) aşağıdaki kısıtlamaları uygular:

Uygulamanız bu amaçlardan herhangi birini kullanıyorsa Android 7.0 veya sonraki sürümleri çalıştıran cihazları düzgün bir şekilde hedefleyebilmek için en kısa sürede bunlara olan bağımlılıkları kaldırmanız gerekir. Android çerçevesi, bu örtülü yayınlara duyulan ihtiyacı azaltmak için çeşitli çözümler sunar. Örneğin, JobScheduler ve yeni WorkManager, belirli koşullar (ör. sınırsız bir ağa bağlantı) karşılandığında ağ işlemlerini planlamak için güçlü mekanizmalar sağlar. Artık JobScheduler kullanarak içerik sağlayıcılarda yapılan değişikliklere de tepki verebilirsiniz. JobInfo nesneleri, JobScheduler işinizi planlamak için kullandığı parametreleri kapsar. İşin koşulları karşılandığında sistem, bu işi uygulamanızın JobService üzerinde yürütür.

Bu sayfada, uygulamanızı bu yeni kısıtlamalara uyarlamak için JobScheduler gibi alternatif yöntemleri nasıl kullanacağınızı öğreneceğiz.

Kullanıcı tarafından başlatılan kısıtlamalar

Kullanıcı, Sistem ayarlarındakiPil kullanımı sayfasında aşağıdaki seçeneklerden birini belirleyebilir:

  • Kısıtlanmamış: Daha fazla pil tüketebilecek tüm arka plan işlemlerine izin verilir.
  • Optimize edilmiş (varsayılan): Kullanıcının uygulamayla etkileşimine göre, uygulamanın arka planda çalışma yapma özelliğini optimize eder.
  • Kısıtlanmış: Uygulamanın arka planda çalışmasını tamamen engeller. Uygulamalar beklendiği gibi çalışmayabilir.

Bir uygulama, Android vitals'da açıklanan kötü davranışlardan bazılarını sergiliyorsa sistem, kullanıcıdan bu uygulamanın sistem kaynaklarına erişimini kısıtlamasını isteyebilir.

Sistem, bir uygulamanın aşırı kaynak tükettiğini fark ederse kullanıcıyı bilgilendirir ve kullanıcının uygulamanın işlemlerini kısıtlamasına olanak tanır. Uyarıyı tetikleyebilecek davranışlar şunlardır:

  • Aşırı sayıda uyanık kalma kilidi: Ekran kapalıyken bir saat boyunca tutulan 1 kısmi uyanık kalma kilidi
  • Aşırı arka plan hizmetleri: Uygulama, 26'dan düşük API düzeylerini hedefliyorsa ve aşırı arka plan hizmetleri varsa

Uygulanan kısıtlamalar, cihaz üreticisi tarafından belirlenir. Örneğin, Android 9 (API düzeyi 28) veya sonraki sürümlerin çalıştığı AOSP derlemelerinde, arka planda çalışan ve "kısıtlanmış" durumda olan uygulamalar aşağıdaki sınırlamalara tabidir:

  • Ön plan hizmetleri başlatılamıyor
  • Mevcut ön plan hizmetleri ön plandan kaldırılır.
  • Alarmlar tetiklenmiyor
  • İşler yürütülmüyor

Ayrıca, Android 13'ü (API düzeyi 33) veya sonraki sürümleri hedefleyen ve"kısıtlanmış" durumda olan bir uygulama başka nedenlerle başlatılana kadar sistem BOOT_COMPLETED yayınını veya LOCKED_BOOT_COMPLETED yayınını teslim etmez.

Belirli kısıtlamalar Güç yönetimiyle ilgili kısıtlamalar bölümünde listelenmiştir.

Ağ etkinliği yayınlarını alma kısıtlamaları

Android 7.0'ı (API düzeyi 24) hedefleyen uygulamalar, manifest dosyalarında CONNECTIVITY_ACTION yayınlarını alacak şekilde kaydolmuş olsalar bile bu yayınları almaz ve bu yayına bağlı olan işlemler başlatılmaz. Bu durum, cihaz sınırsız bir ağa bağlandığında ağ değişikliklerini dinlemek veya toplu ağ etkinlikleri gerçekleştirmek isteyen uygulamalar için sorun oluşturabilir. Bu kısıtlamayı aşmak için Android çerçevesinde halihazırda çeşitli çözümler bulunmaktadır. Ancak doğru çözümü seçmek, uygulamanızın ne yapmasını istediğinize bağlıdır.

Not: BroadcastReceiver ile kaydedilen bir Context.registerReceiver() uygulama çalışırken bu yayınları almaya devam eder.

Ölçülmeyen bağlantılarda ağ işlerini planlama

JobInfo.Builder sınıfını kullanarak JobInfo nesnenizi oluştururken setRequiredNetworkType() yöntemini uygulayın ve JobInfo.NETWORK_TYPE_UNMETERED öğesini iş parametresi olarak iletin. Aşağıdaki kod örneği, cihaz sınırsız bir ağa bağlandığında ve şarj olurken çalışacak bir hizmet planlar:

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MyJobService::class.java)
    )
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
            .setRequiresCharging(true)
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
      (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo job = new JobInfo.Builder(
    MY_BACKGROUND_JOB,
    new ComponentName(context, MyJobService.class))
      .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
      .setRequiresCharging(true)
      .build();
  js.schedule(job);
}

İşinizle ilgili koşullar karşılandığında uygulamanız, belirtilen JobService.class içinde onStartJob() yöntemini çalıştırmak için geri çağırma alır. JobScheduler uygulamasının daha fazla örneğini görmek için JobScheduler örnek uygulamasına bakın.

JobScheduler'a yeni bir alternatif olarak WorkManager'ı kullanabilirsiniz. Bu API, uygulama süreci çalışıyor olsun veya olmasın, tamamlanması garanti edilmesi gereken arka plan görevlerini planlamanıza olanak tanır. WorkManager, işi çalıştırmak için uygun yöntemi (JobScheduler, FirebaseJobDispatcher veya AlarmManager'ı kullanmanın yanı sıra doğrudan uygulamanızın işlemindeki bir iş parçacığında) cihazın API düzeyi gibi faktörlere göre seçer. Ayrıca WorkManager, Play Hizmetleri'ni gerektirmez ve görevleri birbirine bağlama veya görev durumunu kontrol etme gibi çeşitli gelişmiş özellikler sunar. Daha fazla bilgi için WorkManager başlıklı makaleyi inceleyin.

Uygulama çalışırken ağ bağlantısını izleme

Çalışan uygulamalar, kayıtlı bir BroadcastReceiver ile CONNECTIVITY_CHANGE için dinlemeye devam edebilir. Ancak ConnectivityManager API, yalnızca belirtilen ağ koşulları karşılandığında geri arama isteğinde bulunmak için daha güçlü bir yöntem sağlar.

NetworkRequest nesneleri, NetworkCapabilities açısından ağ geri aramasının parametrelerini tanımlar. NetworkRequest.Builder sınıfıyla NetworkRequest nesne oluşturursunuz. registerNetworkCallback() ve NetworkRequest nesnesini sisteme iletir. Ağ koşulları karşılandığında uygulama, onAvailable() sınıfında tanımlanan ConnectivityManager.NetworkCallback yöntemini yürütmek için geri çağırma alır.

Uygulama çıkana veya unregisterNetworkCallback() çağrılana kadar geri çağırmalar almaya devam eder.

Resim ve video yayınları alma ile ilgili kısıtlamalar

Android 7.0'da (API düzeyi 24) uygulamalar ACTION_NEW_PICTURE veya ACTION_NEW_VIDEO yayınları gönderemez ya da alamaz. Bu kısıtlama, yeni bir resim veya videonun işlenmesi için birden fazla uygulamanın uyandırılması gerektiğinde performans ve kullanıcı deneyimi üzerindeki etkileri azaltmaya yardımcı olur. Android 7.0 (API düzeyi 24), alternatif bir çözüm sunmak için JobInfo ve JobParameters'yi genişletir.

İçerik URI'si değişikliklerinde işleri tetikleme

Android 7.0 (API düzeyi 24), içerik URI'si değişikliklerinde işleri tetiklemek için JobInfo API'sini aşağıdaki yöntemlerle genişletir:

JobInfo.TriggerContentUri()
İçerik URI'si değişikliklerinde bir işi tetiklemek için gereken parametreleri kapsar.
JobInfo.Builder.addTriggerContentUri()
TriggerContentUri nesnesini JobInfo'ye iletir. Bir ContentObserver kapsüllenmiş içerik URI'sini izler. Bir işle ilişkilendirilmiş birden fazla TriggerContentUri nesnesi varsa sistem, yalnızca içerik URI'lerinden birinde değişiklik bildirse bile geri çağırma sağlar.
Belirtilen URI'nin alt öğelerinden herhangi biri değişirse işi tetiklemek için TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS işaretini ekleyin. Bu işaret, notifyForDescendants parametresinin registerContentObserver()'ye iletilmesine karşılık gelir.

Not: TriggerContentUri(), setPeriodic() veya setPersisted() ile birlikte kullanılamaz. İçerik değişikliklerini sürekli olarak izlemek için uygulamanın JobInfo en son geri çağırmayı işlemesi bitmeden yeni bir JobService planlayın.

Aşağıdaki örnek kod, sistem içerik URI'sinde değişiklik olduğunu bildirdiğinde tetiklenecek bir iş planlar, MEDIA_URI:

Kotlin

const val MY_BACKGROUND_JOB = 0
...
fun scheduleJob(context: Context) {
    val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val job = JobInfo.Builder(
            MY_BACKGROUND_JOB,
            ComponentName(context, MediaContentJob::class.java)
    )
            .addTriggerContentUri(
                    JobInfo.TriggerContentUri(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
                    )
            )
            .build()
    jobScheduler.schedule(job)
}

Java

public static final int MY_BACKGROUND_JOB = 0;
...
public static void scheduleJob(Context context) {
  JobScheduler js =
          (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  JobInfo.Builder builder = new JobInfo.Builder(
          MY_BACKGROUND_JOB,
          new ComponentName(context, MediaContentJob.class));
  builder.addTriggerContentUri(
          new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
          JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
  js.schedule(builder.build());
}

Sistem, belirtilen içerik URI'lerinde bir değişiklik olduğunu bildirdiğinde uygulamanız geri çağırma alır ve JobParameters nesnesi MediaContentJob.class içindeki onStartJob() yöntemine iletilir.

Hangi içerik yetkililerinin bir işi tetiklediğini belirleme

Android 7.0 (API düzeyi 24), JobParameters işlevini genişleterek uygulamanızın, hangi içerik yetkililerinin ve URI'lerin işi tetiklediği hakkında faydalı bilgiler almasına olanak tanır:

Uri[] getTriggeredContentUris()
İşi tetikleyen URI dizisini döndürür. Bu değer, URI'lerden hiçbiri işi tetiklemediyse (örneğin, iş bir son tarih veya başka bir nedenle tetiklendiyse) ya da değiştirilen URI sayısı 50'den fazlaysa null olur.
String[] getTriggeredContentAuthorities()
İşi tetikleyen içerik yetkililerinin dize dizisini döndürür. Döndürülen dizi null değilse hangi URI'lerin değiştiğiyle ilgili ayrıntıları almak için getTriggeredContentUris() kullanın.

Aşağıdaki örnek kod, JobService.onStartJob() yöntemini geçersiz kılar ve işi tetikleyen içerik yetkililerini ve URI'leri kaydeder:

Kotlin

override fun onStartJob(params: JobParameters): Boolean {
    StringBuilder().apply {
        append("Media content has changed:\n")
        params.triggeredContentAuthorities?.also { authorities ->
            append("Authorities: ${authorities.joinToString(", ")}\n")
            append(params.triggeredContentUris?.joinToString("\n"))
        } ?: append("(No content)")
        Log.i(TAG, toString())
    }
    return true
}

Java

@Override
public boolean onStartJob(JobParameters params) {
  StringBuilder sb = new StringBuilder();
  sb.append("Media content has changed:\n");
  if (params.getTriggeredContentAuthorities() != null) {
      sb.append("Authorities: ");
      boolean first = true;
      for (String auth :
          params.getTriggeredContentAuthorities()) {
          if (first) {
              first = false;
          } else {
             sb.append(", ");
          }
           sb.append(auth);
      }
      if (params.getTriggeredContentUris() != null) {
          for (Uri uri : params.getTriggeredContentUris()) {
              sb.append("\n");
              sb.append(uri);
          }
      }
  } else {
      sb.append("(No content)");
  }
  Log.i(TAG, sb.toString());
  return true;
}

Uygulamanızı daha da optimize etme

Uygulamalarınızı düşük bellekli cihazlarda veya düşük bellek koşullarında çalışacak şekilde optimize etmek performansı ve kullanıcı deneyimini iyileştirebilir. Arka plan hizmetlerine ve manifestte kayıtlı örtülü yayın alıcılarına olan bağımlılıkları kaldırmak, uygulamanızın bu tür cihazlarda daha iyi çalışmasına yardımcı olabilir. Android 7.0 (API seviyesi 24) bu sorunlardan bazılarını azaltmak için adımlar atsa da uygulamanızı bu arka plan işlemlerini hiç kullanmadan çalışacak şekilde optimize etmeniz önerilir.

Aşağıdaki Android Debug Bridge (ADB) komutları, arka plan işlemleri devre dışı bırakılmışken uygulama davranışını test etmenize yardımcı olabilir:

  • Örtülü yayınların ve arka plan hizmetlerinin kullanılamadığı koşulları simüle etmek için şu komutu girin:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Örtülü yayınları ve arka plan hizmetlerini yeniden etkinleştirmek için aşağıdaki komutu girin:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Kullanıcının, uygulamanızı arka planda pil kullanımı için "kısıtlanmış" duruma getirmesini simüle edebilirsiniz. Bu ayar, uygulamanızın arka planda çalışmasını engeller. Bunu yapmak için bir terminal penceresinde aşağıdaki komutu çalıştırın:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny