Arka plan optimizasyonu

Arka plan işlemleri, bellek ve pil kullanımını artırabilir. Örneğin, dolaylı bir yayın, bu yayını dinlemek için kaydolan birçok arka plan işlemini başlatabilir. Bu işlemler çok fazla iş yapmasa bile bu durum geçerlidir. Bu durum, hem cihaz performansını hem de kullanıcı deneyimini önemli ölçüde etkileyebilir.

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

Uygulamanız bu intent'lerden herhangi birini kullanıyorsa Android 7.0 veya sonraki sürümleri çalıştıran cihazları düzgün bir şekilde hedefleyebilmek için bu intent'lere olan bağımlılıkları en kısa sürede kaldırmanız gerekir. Android çerçevesi, bu dolaylı yayınlara olan ihtiyacı azaltmak için çeşitli çözümler sunar. Örneğin, JobScheduler ve yeni WorkManager, sınırsız bir ağa bağlantı gibi belirli koşullar karşılandığında ağ işlemlerini planlamak için güçlü mekanizmalar sağlar. Artık içerik sağlayıcılardaki değişikliklere tepki vermek için JobScheduler da kullanabilirsiniz. JobInfo nesneleri, JobScheduler tarafından işinizi planlamak için kullanılan 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ı öğreneceksiniz.

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

Kullanıcı, sistem ayarlarındaki Pil kullanımı sayfasında aşağıdaki seçeneklerden birini belirleyebilir:

  • Kısıtlanmamış: Arka plandaki tüm çalışmalara izin verilir. Bu, daha fazla pil tüketebilir.
  • Optimize edilmiş (varsayılan): Kullanıcının uygulamayla nasıl etkileşim kurduğuna bağlı olarak uygulamanın arka planda çalışma performansını 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 uygulamanın işlemlerini kısıtlama seçeneği sunar. Bildirimi 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 hedef 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ümleri çalıştıran 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 tetiklenmez.
  • İşler yürütülmüyor

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

Belirli kısıtlamalar Güç yönetimi 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ı almak için kaydolurlarsa bu yayınları almaz ve bu yayına bağlı işlemler başlatılmaz. Bu durum, cihaz tarifesiz 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. Android çerçevesinde bu kısıtlamayı aşmak için kullanılabilecek çeşitli çözümler mevcuttur. Ancak doğru çözümü seçmek, uygulamanızın ne yapmasını istediğinize bağlıdır.

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

Sınırsız bağlantılarda ağ işlerini planlama

JobInfo nesnenizi oluşturmak için JobInfo.Builder sınıfını kullanırken setRequiredNetworkType() yöntemini uygulayın ve iş parametresi olarak JobInfo.NETWORK_TYPE_UNMETERED değerini iletin. Aşağıdaki kod örneği, cihaz ölçümsüz bir ağa bağlandığında ve şarj olurken çalıştırılacak 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);
}

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

JobScheduler'ın yeni bir alternatifi olan WorkManager, uygulama işleminin mevcut olup olmadığına bakılmaksızın tamamlanması garanti edilmesi gereken arka plan görevlerini planlamanıza olanak tanıyan bir API'dir. WorkManager, cihaz API düzeyi gibi faktörlere göre işi çalıştırmanın uygun yolunu seçer (doğrudan uygulama işleminizdeki bir iş parçacığında ve JobScheduler, FirebaseJobDispatcher veya AlarmManager'ı kullanarak). Ayrıca WorkManager, Play Hizmetleri'ni gerektirmez ve görevleri birbirine bağlama veya bir görevin durumunu kontrol etme gibi çeşitli ileri seviye özellikler sunar. Daha fazla bilgi için WorkManager sayfasına bakın.

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'si, yalnızca belirtilen ağ koşulları karşılandığında geri çağırma isteğinde bulunmak için daha sağlam bir yöntem sunar.

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

Uygulama, çıkış yapana veya unregisterNetworkCallback() yöntemini çağırana kadar geri çağırma almaya devam eder.

Resim ve video yayınları alma 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 videoyu işlemek için birkaç uygulamanın uyanması 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'yi 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()
JobInfo işlevine bir TriggerContentUri nesnesi iletir. ContentObserver, kapsüllenmiş içerik URI'sini izler. Bir işle ilişkili birden fazla TriggerContentUri nesnesi varsa sistem, içerik URI'lerinden yalnızca 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, registerContentObserver() işlevine iletilen notifyForDescendants parametresine 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 JobService en son geri çağırma işlemini tamamlamadan önce yeni bir JobInfo planlayın.

Aşağıdaki örnek kod, sistem içerik URI'sinde (MEDIA_URI) bir değişiklik bildirdiğinde tetiklenecek bir iş 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, 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 bir geri çağırma alır ve MediaContentJob.class içindeki onStartJob() yöntemine bir JobParameters nesnesi iletilir.

Bir işi hangi içerik yetkililerinin tetiklediğini belirleme

Android 7.0 (API düzeyi 24), uygulamanızın hangi içerik yetkililerinin ve URI'ların işi tetiklediğiyle ilgili faydalı bilgiler almasına olanak tanımak için JobParameters sınıfını da genişletir:

Uri[] getTriggeredContentUris()
İşi tetikleyen URI'ların dizisini döndürür. Hiçbir URI işi tetiklemediyse (örneğin, iş bir son tarih veya başka bir nedenle tetiklendiyse) ya da değiştirilen URI'ların sayısı 50'den fazlaysa bu değer 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ı dolaylı 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 düzeyi 24) bu sorunlardan bazılarını azaltmak için adımlar atsa da uygulamanızı bu arka plan işlemlerini tamamen kullanmadan çalışacak şekilde optimize etmeniz önerilir.

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

  • Dolaylı yayınların ve arka plan hizmetlerinin kullanılamadığı koşulları simüle etmek için aşağıdaki komutu girin:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
    
  • Dolaylı 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. Bunun için terminal penceresinde aşağıdaki komutu çalıştırın:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny