안드로이드 9) ANR 문제 해결

밍나·2022년 1월 16일
0

Android

목록 보기
9/36

Concepts of ANR

1. ANR
image

  • Activity Not Response의 약어, 에러 상황을 지칭
  • ANR 문제가 발생하는 경우
    • 유저 이벤트에 액티비티가 5초 이내에 반응하지 못하는 경우 발생하는 에러
    • 유저가 Event를 가하는 순간 Event가 EventQueue에 담기고, 시스템에서 EventQueue를 추출해서 이벤트 업무를 진행
    • EventQueue에 담기는 순간부터 5초 이내에 이벤트 업무가 진행되지 않으면 시스템에서 강제로 액티비티를 종료
  • ANR 문제가 발생하는 이유
    • 메인 스레드 : 시스템에서 액티비티를 실행시키는 수행 흐름
    • 하나의 스레드만으로 처리를 하다 보니까 시간이 오래 걸리는 업무가 있으면, 이 업무가 끝나지 않는 한 이벤트 처리가 안되고 ANR 문제가 생길 수 있다
  • ANR 문제의 대표적인 예제 : Network 프로그래밍
  • 해결 방법
    • Thread-Handler로 해결 - Android 11에서 deprecated
    • AsyncTask로 해결 - Android 11에서 deprecated
    • Coroutine으로 해결 - Android 11에서 권장

Handler

1. Handler

  • ANR 문제 해결 방법 : ANR 문제가 스레드가 1개이다 보니 발생하는 에러여서 멀티스레드를 사용
    image
  • Main Thread : Activity를 실행시키는 원래 시스템의 스레드로 화면 출력 및 유저 이벤트 처리
  • Thread : 시간이 오래 걸리는 업무를 처리할 개발자 스레드
    image
  • 개발자 스레드에서 View에 접근하면 에러가 발생, 따라서 개발자 스레드에서는 View 접근이 안되도록 프로그램을 개발해야 함
  • 하지만 Activity에서 작동하는 것이기 때문에 화면과 완전히 독립적일 수 없음
  • 그럼 어떻게 하냐? : 개발자 스레드에서 View에 접근할 일이 있으면 핸들러 클래스를 이용해서 메인 스레드에 의뢰해서 메인 스레드에 의해 View가 관리되도록 함(Thread-Handler)

2. Thread-Handler 사용 방법

  • Handler로 메인 스레드에게 작업을 의뢰하는 방법
    • sendMessage(Message msg) : Message 객체로 메인 스레드에게 작업을 의뢰
    • sendMessageAtFrontOfQueue(Message msg) : View 작업에 대한 의뢰가 반복적으로 발생한 경우 UI Thread에서 순차적으로 처리하는데 이번 의뢰를 가장 먼저 처리하라는 요청
    • sendMessageAtTime(Message msg, long uptimeMillis) : 의뢰를 지정한 시간에 수행
    • sendMessageDelayed(Message msg, long delayMillis) : 지연시간 후에 수행 시켜 달라는 요청
    • sendEmptyMessage(int what) : 데이터 전달 없이 의뢰하는 경우
  • Message 객체 : 메인 스레드에게 넘기는 데이터(아래는 Message 객체의 프로퍼티)
    • what : int형 변수로 구분자, 개발자가 임의 숫자값으로 요청을 구분하기 위해 사용
    • obj : UI Thread에게 넘길 데이터, Object 타입의 변수
    • arg1, arg2 : UI Thread에게 넘길 데이터, int 타입으로 간단한 숫자값은 arg1, arg2 변수에 담아 전달
  • 개발자 스레드 코드(메인 스레드에 데이터 넘기기)
var message = Message()
message.what = 1
message.arg1 = count
handler.sendMessage(message)
  • 메인 스레드 코드(Handler 객체의 handleMessage 호출)
var handler: Handler = objedt: Handler() {
    override fun handleMessage(msg: Message) {
    	if(msg.what === 1) {
            textView.setText(String.valueOf(msg.arg1))
        } else if(msg.what === 2) {
            textView.text = msg.obj as String
        }
    }
}

AsyncTask

1. AsyncTask

  • ANR 문제 해결 방법 : 원론적으로는 Thread-Handler 기법이지만 추상화시켜 조금 더 쉽게 개발할 수 있게 함
  • AsyncTask 클래스를 상속 받아서 개발자 코드 작성(아래는 오버라이드 받아야 하는 함수)
    • doInBackground(Params... params) : Threada에 의해 처리될 내용을 담기 위한 함수
    • onPreExecute() : AsyncTask의 작업을 시작하기 전에 호출, AsyncTask에서 가장 먼저 한 번 호출
    • onPostExecute(Result result) : AsyncTask의 모든 작업이 완료된 후 가장 마지막에 한 번 호출, doInBackground 함수의 최종 값을 받기 위해 사용
    • onProgressUpdate(Progress... values) : doInBackground에 의해 처리되는 중간중간 값을 받아 처리하기 위해서 호출, doInBackground에서 publishProgress 함수로 넘긴 값이 전달
class MyAsyncTask: AsyncTask<Void?, Int?, String>()
  • 스레드에 의해 업무 처리가 되고 그 결과값이 어떤 함수에 넘어갈 때 데이터 타입과 관련있는 것을 제네릭 타입으로 지정해줘야 함
    • 첫번째 타입 : Background 작업을 위한 doInBackground의 매개변수 타입과 동일, AsyncTask에 의해 Background 작업을 의뢰할 때 넘길 데이터의 타입, 없으면 void로 지정
    • 두번째 타입 : doInBackground 함수 수행에 의해 발생한 데이터를 publichProgress() 함수를 이용해 전달하는데 이 때의 전달 데이터 타입, 데이터를 전달 받을 onProgressUpdate 함수의 매개변수 타입과 동일하게 지정
    • 세번째 타입 : onPostExecute의 매개변수 타입과 동일하게 지정, doInBackground 함수의 리턴 타입이며 리턴된 데이터가 onPostExecute 함수의 매개변수로 전달됨

Coroutine

1. Coroutine

  • Non-Blocking, lightweight thread로 일반 스레드처럼 특정 업무를 별개 수행 흐름을 실행시킴
    • thread에 비해 경량이다
    • 메모리 누수가 작다
    • 취소 등 다양한 기법을 지원한다
    • 많은 JetPack 라이브러리에 적용되어 있다

2. Scope

  • 코루틴이 실행되는 영역, 스코프를 만들고 스코프 내에서 코루틴을 시킴
  • 한 스코프 안에 여러 코루틴을 실행시킬 수 있고 각각 또는 전체 코루틴을 제어할 수 있음
  • CoroutineScope의 구현 클래스(인터페이스)
  • 안드로이드에서 GlobalScope, ActorScope, ProducerScope 등 제공
val backgroundScope = CoroutineScope(Dispatchers.Default + Job())
  • Dispatchers : 코루틴을 구동시킬 때 어떤 스레드로 실행시킬 지를 스코프에서 지정하는 것
    • Dispatchers.Main : UI를 출력할 수 있는 액티비티의 메인 스레드에서 동작하는 코루틴을 만들기 위한 디스패처
    • Dispatchers.IO : 파일을 읽기 혹은 쓰기 또는 네트워크 작업 등에 최적화 된 디스패처
    • Dispatchers.Default : CPU를 많이 사용하는 작업을 백그라운드에서 실행할 목적의 디스패처

3. Coroutine 구동 방법

  • Coroutine은 launch, async 등의 함수에 의해 실행
  • launch, async는 Coroutine Builder
backgroundScope.launch{
	// 이 곳에 코루틴(시간이 많이 걸려서 백그라운드에서 실행시킬 기능)을 생성
    // 생성하자마자 launch됨
}
profile
🤗🤗🤗

0개의 댓글