Bir Android uygulamasının kullanıcı arayüzü iş parçacığı çok uzun süre engellendiğinde "Uygulama Yanıt Vermiyor" (ANR) hatası tetiklenir. Uygulama ön plandaysa sistem kullanıcıya Şekil 1'de gösterildiği gibi bir iletişim kutusu görüntüler. ANR iletişim kutusu, kullanıcıya uygulamadan zorla çıkma fırsatı verir.
Uygulamanın kullanıcı arayüzünü güncellemekten sorumlu olan ana iş parçacığı, kullanıcı girişi etkinliklerini veya çizimini işleyemediğinden ANR'ler bir sorun teşkil eder. Bu da kullanıcının hayal kırıklığına uğramasına neden olur. Uygulamanın ana iş parçacığı hakkında daha fazla bilgi için İşlemler ve ileti dizileri bölümüne bakın.
Aşağıdaki koşullardan biri gerçekleştiğinde uygulamanız için bir ANR tetiklenir:
- Giriş gönderme zaman aşımına uğradı: Uygulamanız bir giriş etkinliğine (ör. tuşa basma veya ekrana dokunma) 5 saniye içinde yanıt vermediyse.
- Yürütme hizmeti: Uygulamanız tarafından beyan edilen bir hizmet,
Service.onCreate()
veService.onStartCommand()
/Service.onBind()
yürütmeyi birkaç saniye içinde tamamlayamazsa. - Service.startForeground() çağrılmadı: Uygulamanız ön planda yeni bir hizmet başlatmak için
Context.startForegroundService()
kullanıyorsa ancak hizmet 5 saniye içindestartForeground()
yöntemini çağırmazsa. - Niyet yayını: Bir
BroadcastReceiver
belirli bir süre içinde yürütmeyi tamamlamamışsa. Uygulamanın ön planda herhangi bir etkinliği varsa bu zaman aşımı 5 saniyedir. - JobScheduler etkileşimleri: Bir
JobService
birkaç saniye içindeJobService.onStartJob()
veyaJobService.onStopJob()
ürününden geri dönmezse veya kullanıcı tarafından başlatılan bir iş başlarsa ve uygulamanızJobService.onStartJob()
çağrıldıktan sonra birkaç saniye içindeJobService.setNotification()
çağrısını yapmazsa. Android 13 ve önceki sürümleri hedefleyen uygulamalarda ANR'ler sessizdir ve uygulamaya bildirilmez. Android 14 ve sonraki sürümleri hedefleyen uygulamalarda ANR'ler açıkça anlaşılır ve uygulamaya bildirilir.
Uygulamanızda ANR deneyimi yaşanıyorsa sorunu teşhis edip düzeltmek için bu makaledeki bilgilerden yararlanabilirsiniz.
Sorunu tespit edin
Uygulamanızı zaten yayınladıysanız uygulamanızın ANR'leri ile ilgili bilgileri görmek için Android vitals'ı kullanabilirsiniz. ANR'leri tespit etmek için sahadaki diğer araçları kullanabilirsiniz. Ancak 3. taraf araçların, Android vitals'ın aksine Android'in eski sürümlerindeki (Android 10 ve önceki sürümler) ANR'leri bildiremeyeceğini unutmayın.
Android vitals
Android vitals, uygulamanızın ANR oranını izlemenize ve iyileştirmenize yardımcı olabilir. Android vitals çeşitli ANR oranlarını ölçer:
- ANR oranı: Günlük etkin kullanıcılarınız arasında, herhangi bir türde ANR yaşayanların yüzdesidir.
- Kullanıcı tarafından algılanan ANR oranı: Günlük etkin kullanıcılarınız arasında, en az bir kullanıcı tarafından algılanan ANR yaşayanların yüzdesidir. Şu anda yalnızca
Input dispatching timed out
türündeki ANR'ler kullanıcı tarafından algılanan olarak kabul edilmektedir. - Çoklu ANR oranı: Günlük etkin kullanıcılarınız arasında en az iki ANR yaşayanların yüzdesidir.
Günlük etkin kullanıcı, uygulamanızı tek bir gün içinde tek bir cihazda ve potansiyel olarak birden fazla oturumda kullanan tekil bir kullanıcıdır. Bir kullanıcı aynı günde uygulamanızı birden fazla cihazda kullanırsa her cihaz o günün etkin kullanıcı sayısına eklenir. Birden fazla kullanıcı tek bir gün içinde aynı cihazı kullanırsa bu, tek bir etkin kullanıcı olarak sayılır.
Kullanıcı tarafından algılanan ANR oranı önemli bir metriktir. Yani Google Play'de uygulamanızın keşfedilebilirliğini etkiler. Bu metriğin saydığı ANR'lerin her zaman kullanıcılar uygulamayla etkileşim halindeyken gerçekleşerek çok fazla aksamaya yol açması, bu metriği önemli kılar.
Play bu metrikte iki kötü davranış eşiği belirlemiştir:
- Genel kötü davranış eşiği: Tüm cihaz modellerinde, günlük etkin kullanıcıların en az% 0,47'si, kullanıcı tarafından algılanan ANR yaşamıştır.
- Cihaz bazında kötü davranış eşiği: Günlük kullanıcıların en az% 8'i tek bir cihaz modeli için, kullanıcı tarafından algılanan ANR yaşamıştır.
Genel kötü davranış eşiğini aşan uygulamaların tüm cihazlarda bulunabilirliği azalabilir. Uygulamanız bazı cihazlarda cihaz başına kötü davranış eşiğini aşarsa bu cihazlardaki bulunabilirliği azalabilir ve mağaza girişinizde bir uyarı gösterilebilir.
Uygulamanız aşırı sayıda ANR gösterdiğinde Android vitals, Play Console aracılığıyla sizi uyarabilir.
Google Play'in Android vitals verilerini nasıl topladığı hakkında bilgi için Play Console belgelerine bakın.
ANR'leri teşhis etme
ANR'leri teşhis ederken yaygın olarak bakılacak bazı kalıplar vardır:
- Uygulama, ana iş parçacığında G/Ç içeren yavaş işlemler gerçekleştiriyor.
- Uygulama, ana iş parçacığında uzun bir hesaplama yapıyor.
- Ana iş parçacığı başka bir işleme eşzamanlı bir bağlayıcı çağrısı yapıyor ve diğer işlemin geri dönüşü uzun sürüyor.
- Ana iş parçacığı, başka bir iş parçacığında gerçekleşen uzun bir işlem için senkronize edilmiş bir blok beklenirken engellenir.
- Ana iş parçacığı, işleminizde veya bir bağlayıcı çağrısı aracılığıyla başka bir iş parçacığı ile kilitlenmededir. Ana iş parçacığı sadece uzun bir işlemin tamamlanmasını beklemekle kalmıyor, aynı zamanda bir kilitlenmede de bulunuyor. Daha fazla bilgi için Wikipedia'daki Kilitlenme bölümüne bakın.
Aşağıdaki teknikler, ANR'lerinizin nedenini belirlemenize yardımcı olabilir.
Sağlık İstatistikleri
HealthStats
toplam kullanıcı ve sistem süresini, CPU saatini, ağ, radyo istatistiklerini, ekran açılma/kapanma zamanını ve uyandırma alarmlarını yakalayarak uygulamanın sağlık durumu hakkında metrikler sağlar. Bu, genel CPU kullanımını ve pil tüketimini ölçmeye yardımcı olabilir.
Hata ayıkla
Debug
, uygulamalardaki olumsuzluk ve gecikmeleri belirlemek için izleme ve ayırma sayıları dahil olmak üzere geliştirme sırasında Android uygulamalarının denetlenmesine yardımcı olur. Ayrıca, çalışma zamanı ve yerel bellek sayaçlarının yanı sıra belirli bir işlemin bellek ayak izini tanımlamaya yardımcı olabilecek bellek metrikleri edinmek için Debug
öğesini de kullanabilirsiniz.
UygulamaÇıkış Bilgisi
ApplicationExitInfo
, Android 11 (API düzeyi 30) veya sonraki sürümlerde kullanılabilir ve uygulamadan çıkma nedeni hakkında bilgi sağlar. Buna ANR'ler, düşük bellek, uygulama kilitlenmeleri, aşırı CPU kullanımı, kullanıcı kesintileri, sistem kesintileri veya çalışma zamanı izni değişiklikleri dahildir.
Yüksek güvenlik modu
StrictMode
kullanmak, uygulamanızı geliştirirken ana iş parçacığında yanlışlıkla yapılan G/Ç işlemlerini bulmanıza yardımcı olur. StrictMode
'i uygulama veya etkinlik düzeyinde kullanabilirsiniz.
Arka plan ANR iletişim kutularını etkinleştir
Android, yayın mesajını işlemesi çok uzun süren uygulamalar için ANR iletişim kutularını yalnızca cihazın Geliştirici seçeneklerinde Tüm ANR'leri göster etkinleştirilmişse gösterir. Bu nedenle, arka plan ANR iletişim kutuları kullanıcıya her zaman gösterilmese de uygulamada hâlâ performans sorunları yaşanıyor olabilir.
İzleme Görünümü
Kullanım alanlarını incelerken çalışan uygulamanızın izini almak ve ana iş parçacığının meşgul olduğu yerleri tanımlamak için Traceview'u kullanabilirsiniz. Traceview'un nasıl kullanılacağı hakkında bilgi için Traceview ve dmtracedump ile profil oluşturma bölümüne bakın.
İzleme dosyasını alma
Android, ANR yaşadığında iz bilgilerini depolar. Daha eski işletim sistemi sürümlerinde cihazda tek bir /data/anr/traces.txt
dosyası bulunur.
Daha yeni OS sürümlerinde birden fazla /data/anr/anr_*
dosyası vardır.
Android Debug Bridge (adb) uygulamasını kök olarak kullanarak bir cihazdan veya emülatörden ANR izlerine erişebilirsiniz:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
Fiziksel bir cihazdan hata raporu almak için cihazdaki Hata raporu geliştiriciyi al seçeneğini veya geliştirme makinenizdeki adb hata raporu komutunu kullanabilirsiniz. Daha fazla bilgi için Hata raporlarını yakalama ve okuma konusuna bakın.
Sorunları düzeltme
Sorunu belirledikten sonra, sık karşılaşılan sorunları düzeltmek için bu bölümdeki ipuçlarından yararlanabilirsiniz.
Ana iş parçacığında yavaş kod var
Kodunuzda, uygulamanın ana iş parçacığının 5 saniyeden uzun süre meşgul olduğu yerleri belirleyin. Uygulamanızdaki şüpheli kullanım alanlarını bulun ve ANR'yi yeniden oluşturmaya çalışın.
Örneğin, Şekil 2'de ana iş parçacığının 5 saniyeden uzun süre meşgul olduğu bir Traceview zaman çizelgesi gösterilmektedir.
Şekil 2, aşağıdaki kod örneğinde gösterildiği gibi, rahatsız edici kodun çoğunun onClick(View)
işleyicide gerçekleştiğini gösterir:
Kotlin
override fun onClick(v: View) { // This task runs on the main thread. BubbleSort.sort(data) }
Java
@Override public void onClick(View view) { // This task runs on the main thread. BubbleSort.sort(data); }
Bu durumda, ana iş parçacığında çalışan çalışmayı bir çalışan iş parçacığına taşımanız gerekir. Android Framework, görevi bir çalışan iş parçacığına taşımaya yardımcı olabilecek sınıflar içerir. Daha fazla bilgi için Çalışan iş parçacıkları bölümüne bakın.
Ana iş parçacığında KS
Ana iş parçacığında IO işlemleri yürütmek, ana iş parçacığında yavaş işlemlerin yaygın nedenlerinden biridir ve bu da ANR'lere neden olabilir. Önceki bölümde gösterildiği gibi, tüm KS işlemlerinin bir çalışan iş parçacığına taşınması önerilir.
G/Ç işlemlerine bazı örnek ağ ve depolama işlemleridir. Daha fazla bilgi için Ağ işlemlerini gerçekleştirme ve Veri tasarrufu konularına bakın.
Kilit anlaşmazlığı
Bazı senaryolarda, ANR'ye neden olan çalışma doğrudan uygulamanın ana iş parçacığında yürütülmez. Bir çalışan iş parçacığı, ana iş parçacığının çalışmasını tamamlamak için gerektirdiği bir kaynakta kilit tutarsa ANR gerçekleşebilir.
Örneğin, Şekil 4'te işin çoğunun bir çalışan iş parçacığında gerçekleştirildiği bir Traceview zaman çizelgesi gösterilmektedir.
4. Şekil. Bir çalışan iş parçacığında yürütülen çalışmayı gösteren Traceview zaman çizelgesi
Ancak kullanıcılarınız hâlâ ANR sorunu yaşıyorsa Android Device Monitor'da ana iş parçacığının durumuna bakmanız gerekir. Ana iş parçacığı genellikle kullanıcı arayüzünü güncellemeye hazırsa ve genellikle duyarlıysa RUNNABLE
durumundadır.
Ancak ana iş parçacığı yürütmeyi devam ettiremezse BLOCKED
durumunda olur ve etkinliklere yanıt veremez. Durum, Android Cihaz Monitörü'nde Şekil 5'te gösterildiği gibi Monitor veya Bekle olarak gösterilir.
Aşağıdaki iz, bir uygulamanın kaynak beklerken engellenen ana iş parçacığını gösterir:
...
AsyncTask #2" prio=5 tid=18 Runnable
| group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100
| sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920
| state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100
| stack=0x94a7e000-0x94a80000 stackSize=1038KB
| held mutexes= "mutator lock"(shared held)
at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147)
- locked <0x083105ee> (a java.lang.Boolean)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
...
İzi incelemek, ana iş parçacığını engelleyen kodu bulmanıza yardımcı olabilir. Aşağıdaki kod, önceki izdeki ana iş parçacığını engelleyen kilidi tutmaktan sorumludur:
Kotlin
override fun onClick(v: View) { // The worker thread holds a lock on lockedResource LockTask().execute(data) synchronized(lockedResource) { // The main thread requires lockedResource here // but it has to wait until LockTask finishes using it. } } class LockTask : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? = synchronized(lockedResource) { // This is a long-running operation, which makes // the lock last for a long time BubbleSort.sort(params[0]) } }
Java
@Override public void onClick(View v) { // The worker thread holds a lock on lockedResource new LockTask().execute(data); synchronized (lockedResource) { // The main thread requires lockedResource here // but it has to wait until LockTask finishes using it. } } public class LockTask extends AsyncTask<Integer[], Integer, Long> { @Override protected Long doInBackground(Integer[]... params) { synchronized (lockedResource) { // This is a long-running operation, which makes // the lock last for a long time BubbleSort.sort(params[0]); } } }
Diğer bir örnek de aşağıdaki kodda gösterildiği gibi, uygulamanın bir çalışan iş parçacığından sonuç bekleyen ana iş parçacığıdır. Eşzamanlılığı yönetmek için kendi mekanizmaları olan Kotlin'de wait()
ve notify()
kullanımının önerilen bir kalıp olmadığını unutmayın. Kotlin kullanırken mümkünse Kotlin'e özgü mekanizmalar kullanmalısınız.
Kotlin
fun onClick(v: View) { val lock = java.lang.Object() val waitTask = WaitTask(lock) synchronized(lock) { try { waitTask.execute(data) // Wait for this worker thread’s notification lock.wait() } catch (e: InterruptedException) { } } } internal class WaitTask(private val lock: java.lang.Object) : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? { synchronized(lock) { BubbleSort.sort(params[0]) // Finished, notify the main thread lock.notify() } } }
Java
public void onClick(View v) { WaitTask waitTask = new WaitTask(); synchronized (waitTask) { try { waitTask.execute(data); // Wait for this worker thread’s notification waitTask.wait(); } catch (InterruptedException e) {} } } class WaitTask extends AsyncTask<Integer[], Integer, Long> { @Override protected Long doInBackground(Integer[]... params) { synchronized (this) { BubbleSort.sort(params[0]); // Finished, notify the main thread notify(); } } }
Lock
ve Semaphore
kullanan iş parçacıklarının yanı sıra kaynak havuzu (veritabanı bağlantıları havuzu gibi) veya diğer karşılıklı hariç tutma (mutex) mekanizmalarını kullanan iş parçacıkları da dahil olmak üzere ana iş parçacığını engelleyebilecek bazı başka durumlar vardır.
Uygulamanızın kaynaklar üzerinde tuttuğu kilitleri genel olarak değerlendirmeniz gerekir ancak ANR'leri önlemek istiyorsanız ana iş parçacığının gerektirdiği kaynaklar için tutulan kilitlere bakmanız gerekir.
Kilitlerin en kısa süre boyunca tutulduğundan emin olun. Daha da iyisi, uygulamanın askıda tutulması gerekip gerekmediğini değerlendirin. Bir çalışan iş parçacığının işlenmesine dayalı olarak kullanıcı arayüzünün ne zaman güncelleneceğini belirlemek için kilit kullanıyorsanız çalışan iş parçacığı ile ana iş parçacıkları arasında iletişim kurmak için onProgressUpdate()
ve onPostExecute()
gibi mekanizmalar kullanın.
Kilitlenmeler
Gerekli bir kaynak, aynı zamanda ilk iş parçacığı tarafından barındırılan bir kaynağı bekleyen başka bir iş parçacığı tarafından tutulduğu için bir iş parçacığı bekleme durumuna girdiğinde kilitlenme kilitlenir. Uygulamanın ana iş parçacığı bu durumdaysa ANR'ler gerçekleşme olasılığı yüksektir.
Kilitlenmeler, bilgisayar biliminde uzun süredir iyi çalışılan bir olaydır ve kilitlenmelerden kaçınmak için kullanabileceğiniz kilitlenme önleme algoritmaları vardır.
Daha fazla bilgi için Wikipedia'daki Kilitlenme ve Kilitlenme önleme algoritmaları bölümlerine bakın.
Yavaş yayın alıcıları
Uygulamalar, yayın alıcıları aracılığıyla uçak modunu etkinleştirme veya devre dışı bırakma ya da bağlantı durumunda değişiklik gibi yayın mesajlarına yanıt verebilir. Bir uygulamanın yayın mesajını işlemesi çok uzun sürdüğünde ANR meydana gelir.
ANR aşağıdaki durumlarda gerçekleşir:
- Yayın alıcısı,
onReceive()
yöntemini uzun bir süre içinde yürütmeyi tamamlamamıştır. - Bir yayın alıcısı
goAsync()
yöntemini çağırır vePendingResult
nesnesindefinish()
çağrısında bulunamaz.
Uygulamanız, BroadcastReceiver
onReceive()
yönteminde yalnızca kısa işlemler gerçekleştirmelidir.
Ancak, uygulamanız bir yayın mesajı nedeniyle daha karmaşık işleme gerektiriyorsa görevi bir IntentService
'a ertelemeniz gerekir.
Yayın alıcınızın, uygulamanın ana iş parçacığında uzun süreli işlemler yürütüp yürütmediğini belirlemek için Traceview gibi araçları kullanabilirsiniz. Örneğin, Şekil 6'da ana iş parçacığında bir mesajı yaklaşık 100 saniye boyunca işleyen bir yayın alıcısının zaman çizelgesi gösterilmektedir.
Bu davranış, aşağıdaki örnekte gösterildiği gibi BroadcastReceiver
için onReceive()
yönteminde uzun süreli işlemler yürütmeden kaynaklanabilir:
Kotlin
override fun onReceive(context: Context, intent: Intent) { // This is a long-running operation BubbleSort.sort(data) }
Java
@Override public void onReceive(Context context, Intent intent) { // This is a long-running operation BubbleSort.sort(data); }
Bu gibi durumlarda, uzun süreli işlemin bir IntentService
öğesine taşınması önerilir çünkü bu işlem, çalışmasını yürütmek için çalışan iş parçacığı kullanır. Aşağıdaki kod, uzun süreli bir işlemi işlemek için IntentService
'in nasıl kullanılacağını gösterir:
Kotlin
override fun onReceive(context: Context, intent: Intent) { Intent(context, MyIntentService::class.java).also { intentService -> // The task now runs on a worker thread. context.startService(intentService) } } class MyIntentService : IntentService("MyIntentService") { override fun onHandleIntent(intent: Intent?) { BubbleSort.sort(data) } }
Java
@Override public void onReceive(Context context, Intent intent) { // The task now runs on a worker thread. Intent intentService = new Intent(context, MyIntentService.class); context.startService(intentService); } public class MyIntentService extends IntentService { @Override protected void onHandleIntent(@Nullable Intent intent) { BubbleSort.sort(data); } }
IntentService
kullanılması sonucunda, uzun süreli işlem ana iş parçacığı yerine bir çalışan iş parçacığında yürütülür. Şekil 7'de, Traceview zaman çizelgesinde çalışan iş parçacığına ertelenen çalışma gösterilmektedir.
Yayın alıcınız, mesajı işlemesinin daha fazla zamana ihtiyacı olduğunu sisteme bildirmek için goAsync()
aracını kullanabilir. Ancak PendingResult
nesnesinde finish()
çağrısı yapmanız gerekir. Aşağıdaki örnekte, sistemin yayın alıcısını geri dönüştürmesini sağlamak ve ANR'yi önlemek için final() çağrısının nasıl yapılacağı gösterilmektedir:
Kotlin
val pendingResult = goAsync() object : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? { // This is a long-running operation BubbleSort.sort(params[0]) pendingResult.finish() return 0L } }.execute(data)
Java
final PendingResult pendingResult = goAsync(); new AsyncTask<Integer[], Integer, Long>() { @Override protected Long doInBackground(Integer[]... params) { // This is a long-running operation BubbleSort.sort(params[0]); pendingResult.finish(); } }.execute(data);
Ancak yayın arka plandayken kodun yavaş bir yayın alıcısından başka bir iş parçacığına taşınması ve goAsync()
kullanılması ANR'yi düzeltmez.
ANR zaman aşımı hâlâ geçerlidir.
Oyun Etkinliği
GameActivity
kitaplığı, C veya C++'ta yazılmış oyun ve uygulamalarla ilgili örnek olaylarda ANR'leri azaltmıştır. Mevcut yerel etkinliğinizi GameActivity
ile değiştirirseniz kullanıcı arayüzü iş parçacığı engellemeyi azaltabilir ve bazı ANR'lerin meydana gelmesini engelleyebilirsiniz.
ANR'ler hakkında daha fazla bilgi için Uygulamanızı duyarlı tutma konusuna bakın. İş parçacıkları hakkında daha fazla bilgi için İleti dizisi performansı bölümüne bakın.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- Aşırı sayıda uyandırma