ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Anr 예제
    안드로이드 학습/Android 기술면접 대비 코드 2024. 1. 23. 10:48

    Anr 설명 : https://from-android-to-server.tistory.com/41

    github : https://github.com/tvroom88/AIO_Android_Kotlin_Support_Material/tree/main/AnrTest/AnrTest

     

    안드로이드 개발자 페이지에서 보면

    다음 조건 중 하나가 발생하면 앱과 관련한 ANR이 트리거됩니다.
    • 입력 전달 타임아웃: 앱이 입력 이벤트(예: 키 누름 또는 화면 터치)에 5초 이내에 응답하지 않은 경우
    • 서비스 실행: 앱에서 선언한 서비스가 몇 초 이내에 Service.onCreate() 및 Service.onStartCommand()/Service.onBind() 실행을 완료할 수 없는 경우
    • Service.startForeground()가 호출되지 않음: 앱이 Context.startForegroundService()를 사용하여 포그라운드에서 새 서비스를 시작했지만 서비스가 5초 내에 startForeground()를 호출하지 않은 경우
    • 인텐트 브로드캐스트: BroadcastReceiver가 설정된 시간 내에 실행을 완료하지 못한 경우. 앱에 포그라운드 활동이 있는 경우 이 제한 시간은 5초입니다.
    • JobScheduler 상호작용: JobService가 몇 초 이내에 JobService.onStartJob() 또는 JobService.onStopJob()에서 반환되지 않거나 사용자 시작 작업이 시작되고 JobService.onStartJob()이 호출된 후 몇 초 이내에 앱이 JobService.setNotification()을 호출하지 않는 경우. Android 13 및 이전 버전을 타겟팅하는 앱의 경우 ANR이 음소거되고 앱에 보고되지 않습니다. Android 14 및 이후 버전을 타겟팅하는 앱의 경우 ANR이 명시적이고 앱에 보고됩니다.

     

    여태까지 앱을 만들어 보면서 ANR이 걸려볼 일이 없어서 직접 테스트 해보기로 했다 

     

    1. 입력 전달 타임아웃: 앱이 입력 이벤트(예: 키 누름 또는 화면 터치)에 5초 이내에 응답하지 않은 경우

     

    5초 이상 걸리는 작업을 만들었다. 

    // 1. 입력 전달 타임아웃
    fun plus(): String {
        var number = 0
        for (i in Int.MIN_VALUE + 0..Int.MAX_VALUE) {
            number = i
        }
        return number.toString()
    }
    

     

    그리고 버튼 클릭시  작업이 수행되게 하고 그 다음 다른 버튼을 눌러보면

    countBtn.setOnClickListener {
        plus()
    }

     

    2. background service에서도 onCreate 수행하면서 오래 걸린다며 ANR이 발생했다.

    다만 5초는 아니고 더 오래 걸렸던 것 같다.

    class BackgroundService : Service() {
    
        override fun onCreate() {
            super.onCreate()
            Log.d("BackgroundService", "onCreate")
    
            plus()
            plus()
            plus()
        }
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            Log.d("BackgroundService", "onStartCommand")
            Toast.makeText(this, "called onStartCommand", Toast.LENGTH_SHORT).show()
            return START_STICKY
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.d("BackgroundService", "onDestroy")
            Toast.makeText(this, "called onDestroy", Toast.LENGTH_SHORT).show()
        }
    
        @Nullable
        override fun onBind(intent: Intent?): IBinder? {
            return null
        }
    
        fun plus(): String {
            var number = 0
            for (i in Int.MIN_VALUE + 0..Int.MAX_VALUE) {
                number = i
            }
            return number.toString()
        }
    }

     

    3. Foreground Service에서는 서비스가 시작하고 서비스 내부에서 서비스 시작하는 것을 연결시켜 주지 않으니 ANR이 발생했다. 

    startForeground(1, notification.build())
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    
        Log.d(TAG, "ForegroundService - onStartCommand")
    
        when (intent.action) {
    
            // startService()에 의해 서비스가 시작될 때
            "StartForeground" -> {
                Log.d(TAG, "ForegroundService - StartForeground")
    
                val inputTitle = intent.getStringExtra(Noti_Title)
                val inputContent = intent.getStringExtra(Noti_Content)
    
                //안드로이드 sdk 26버전 이상에서는 알림창을 띄워야 Foreground
                createNotificationChannel()
    
                // Notification 클릭시 MainActivity 이동
                val notificationIntent = Intent(this, MainActivity::class.java)
                val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
    
                // 버튼 클릭시 Foreground Service 종료
                val stopServiceIntent = Intent(this, ForegroundService::class.java)
                stopServiceIntent.action = ("StopService")
                val stopPendingIntent = PendingIntent.getService(this, 0, stopServiceIntent,
                    PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE)
    
                notification = NotificationCompat.Builder(this, CHANNEL_ID).apply {
                    setSmallIcon(R.drawable.ic_launcher_foreground)
                    setContentTitle(inputTitle)
                    setContentText(inputContent)
                    setStyle(NotificationCompat.BigTextStyle().bigText(inputContent))
                    addAction(R.drawable.ic_launcher_foreground, "stop service", stopPendingIntent)
                    setContentIntent(pendingIntent)
                }
    
                startForeground(1, notification.build())
            }
    
    
            "StopService" -> {
                Log.d(TAG, "ForegroundService - stopSelf")
    
                // 서비스를 정지시키는 로직을 구현합니다.
                stopForeground(STOP_FOREGROUND_REMOVE)
                stopSelf()
            }
    
        }
    
        return START_STICKY
    }

     

    이렇게 ANR을 강제로 해보면서 느낀건 진짜 큰 데이터를 다루는 것이 아닌 이상 일부로 해도 힘들기도 하고 엄청 많은 데이터를 다룬다고 해도 당연하게 멀티 쓰레드나 코루틴을 이용하기 때문에 굳이 ANR 걱정은 할 필요는 없을 것 같다는 생각을 들게 했다. 

Designed by Tistory.