Kavramlar ve Jetpack Compose uygulaması
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, Şekil 1'de gösterildiği gibi kullanıcıya bir iletişim kutusu gösterir. ANR iletişim kutusu, kullanıcıya uygulamayı kapanmaya zorlama fırsatı verir.
ANR'ler, uygulamanın kullanıcı arayüzünü güncellemekten sorumlu ana iş parçacığının kullanıcı girişi etkinliklerini işleyememesi veya çizememesi nedeniyle kullanıcıda hayal kırıklığına yol açan bir sorundur. Uygulamanın ana iş parçacığı hakkında daha fazla bilgi için İşlemlere ve iş parçacıklarına genel bakış başlıklı makaleyi inceleyin.
Aşağıdaki durumlardan 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.
- Hizmet yürütme: Uygulamanız tarafından beyan edilen bir hizmet, birkaç saniye içinde
Service.onCreate()veService.onStartCommand()/Service.onBind()yürütmeyi tamamlayamazsa. **Service.startForeground()çağrılmadı**: Uygulamanız ön planda yeni bir hizmet başlatmak içinContext.startForegroundService()kullanıyor ancak hizmet 5 saniye içindestartForeground()çağrısı yapmıyor.- Amaç yayını: Bir
BroadcastReceiver, belirlenen süre içinde yürütmeyi tamamlamadıysa. Uygulamanın ön planda herhangi bir etkinliği varsa bu zaman aşımı 10 saniyedir. **JobScheduleretkileşimleri**: BirJobService, birkaç saniye içindeJobService.onStartJob()veyaJobService.onStopJob()'ten dönmezse ya da kullanıcı tarafından başlatılan bir iş başlarsa ve uygulamanız,JobService.onStartJob()çağrıldıktan sonra birkaç saniye içindeJobService.setNotification()'i çağırmazsa. 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çıktır ve uygulamaya bildirilir.
Uygulamanızda ANR'ler yaşanıyorsa sorunu teşhis edip düzeltmek için bu makaledeki yönergeleri kullanabilirsiniz.
Sorunları düzeltme
Sorunu belirledikten sonra, sık karşılaşılan sorunları düzeltmek için bu bölümdeki ipuçlarını kullanabilirsiniz.
Ana iş parçacığında yavaş kod
Kodunuzda, uygulamanın ana iş parçacığının 5 saniyeden uzun süre meşgul olduğu yerleri belirleyin. Uygulamanızda şüpheli kullanım alanlarını arayın 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. Meşgul bir ana ileti dizisini gösteren Traceview zaman çizelgesi
Şekil 2, sorunlu kodun çoğunun aşağıdaki kod örneğinde gösterildiği gibi onClick(View) işleyicisinde olduğunu 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 işi bir çalışan iş parçacığına taşımanız gerekir. Android çerçevesi, görevi ç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 G/Ç
Ana iş parçacığında G/Ç işlemlerinin yürütülmesi, ana iş parçacığındaki işlemlerin yavaşlamasının yaygın bir nedenidir ve ANR'lere yol açabilir. Oluşturma'da geliştiriciler, ilk durumu türetmeye çalışırken genellikle yanlışlıkla disk okuma işlemlerini (SharedPreferences veya veritabanı çağrıları gibi) tetikler.
Uzun süren G/Ç işlemlerini kullanıcı arayüzü katmanından uzakta yürütün. withContext(Dispatchers.IO)'yi ViewModel içinde kullanın veya daha da iyisi, veri katmanında bir depo kullanın. Önceki bölümde gösterildiği gibi, tüm G/Ç işlemlerini bir çalışan iş parçacığına taşımanız önerilir.
G/Ç işlemlerine örnek olarak ağ ve depolama işlemleri verilebilir. Daha fazla bilgi için Ağ işlemleri gerçekleştirme ve Veri kaydetme başlıklı makaleleri inceleyin.
Kilit anlaşmazlığı
Bazı senaryolarda ANR'ye neden olan çalışma doğrudan uygulamanın ana iş parçacığında yürütülmez. Çalışan iş parçacığı, ana iş parçacığının çalışmasını tamamlaması için gereken bir kaynakta kilit tutuyorsa ANR oluşabilir.
Örneğin, Şekil 3'te, işlerin çoğunun bir çalışan iş parçacığında gerçekleştirildiği bir Traceview zaman çizelgesi gösterilmektedir.

Şekil 3. Ç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'lerle karşılaşıyorsa Android Device Monitor'da ana iş parçacığının durumuna bakmanız gerekir. Genellikle ana iş parçacığı, kullanıcı arayüzünü güncellemeye hazırsa RUNNABLE durumundadır ve genellikle yanıt verir.
Ancak ana iş parçacığı yürütmeye devam edemezse BLOCKED durumundadır ve etkinliklere yanıt veremez. Durum, Şekil 5'te gösterildiği gibi Android cihaz Monitörü'nde Monitor (İzle) veya Wait (Bekle) olarak gösterilir.

4. şekil. Ana iş parçacığı "İzleme" durumunda
Aşağıdaki iz, bir kaynağı beklerken engellenen bir uygulamanın ana iş parçacığını göstermektedir:
...
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)
...
İzlemeyi incelemek, ana iş parçacığını engelleyen kodu bulmanıza yardımcı olabilir. Aşağıdaki kod, önceki izlemede 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]);
}
}
}
Başka bir örnek de aşağıdaki kodda gösterildiği gibi, bir çalışan iş parçacığından sonuç bekleyen bir uygulamanın ana iş parçacığıdır. wait() ve notify() kullanımı, eşzamanlılığı yönetmek için kendi mekanizmalarına sahip olan Kotlin'de önerilen bir kalıp değildir. Kotlin kullanırken mümkünse Kotlin'e özgü mekanizmaları kullanmanız gerekir.
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, Semaphore kullanan iş parçacıklarının yanı sıra bir kaynak havuzu (ör. veritabanı bağlantıları havuzu) veya diğer karşılıklı dışlama (mutex) mekanizmaları dahil olmak üzere ana iş parçacığını engelleyebilecek başka durumlar da vardır.
Uygulamanızın genel olarak kaynaklarda tuttuğu kilitleri değerlendirmeniz gerekir ancak ANR'leri önlemek istiyorsanız ana iş parçacığının ihtiyaç duyduğu kaynaklar için tutulan kilitlere bakmanız gerekir.
Kilitlerin mümkün olan en kısa süre boyunca tutulduğundan emin olun. Hatta uygulamanın kilidi tutması gerekip gerekmediğini değerlendirin. Kullanıcı arayüzünün ne zaman güncelleneceğini çalışan iş parçacığının işlenmesine göre belirlemek için kilidi kullanıyorsanız çalışan iş parçacığı ile ana iş parçacığı arasında iletişim kurmak için onProgressUpdate() ve onPostExecute() gibi mekanizmalar kullanı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ı durumundaki bir değişiklik gibi yayın mesajlarına yanıt verebilir. Uygulamanın, yayın mesajını işlemesi çok uzun sürdüğünde ANR meydana gelir.
ANR aşağıdaki durumlarda gerçekleşir:
- Bir yayın alıcısı, makul bir süre içinde
onReceiveyöntemini yürütmeyi tamamlamadı. - Yayın alıcısı,
PendingResultnesnesindegoAsyncyöntemini çağırıyor ancakfinishyöntemini çağıramıyor.
Uygulamanız, bir BroadcastReceiver öğesinin onReceive yönteminde yalnızca kısa işlemler gerçekleştirmelidir. Ancak uygulamanız, yayın mesajı sonucunda daha karmaşık bir işlem gerektiriyorsa görevi ViewModel (Kotlin eş yordamlarının, kapsamlarının ve dağıtıcılarının gücünden yararlanarak) en fazla birkaç saniye sürmesi beklenen görevler için herhangi bir durum bilgisi depolayıcıya veya birkaç saniyeden uzun sürmesi beklenen görevler için WorkManager'e 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 yaklaşık 100 saniye boyunca bir mesajı işleyen bir yayın alıcısının zaman çizelgesi gösterilmektedir.

Şekil 5. Ana iş parçacığında BroadcastReceiver çalışmasını gösteren Traceview zaman çizelgesi
Bu davranış, aşağıdaki örnekte gösterildiği gibi BroadcastReceiver sınıfının onReceive() yönteminde uzun süreli işlemler yürütülmesinden 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şlemi bir IntentService'e taşımanız önerilir. Çünkü bu işlem, işini yürütmek için çalışan iş parçacığı kullanır.
Aşağıdaki kodda, uzun süreli bir işlemi işlemek için IntentService'nin nasıl kullanılacağı gösterilmektedir:
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ıldığında uzun süreli işlem, ana iş parçacığı yerine ç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.

6. Şekil. Çalışan iş parçacığında işlenen yayın mesajını gösteren Traceview zaman çizelgesi
Yayın alıcınız, mesajı işlemek için daha fazla zamana ihtiyacı olduğunu sisteme bildirmek için goAsync() yöntemini kullanabilir. Ancak PendingResult nesnesinde finish() yöntemini çağırmanız gerekir. Aşağıdaki örnekte, sistemin yayın alıcıyı geri dönüştürmesine izin vermek ve ANR'yi önlemek için finish() yönteminin nasıl çağrı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ın arka planda olması durumunda kodu yavaş bir yayın alıcıdan başka bir iş parçacığına taşımak ve goAsync() kullanmak ANR'yi düzeltmez.
ANR zaman aşımı hâlâ geçerlidir.