안드로이드 스튜디오 태스크 관리, 액티비티 ANR 문제와 코루틴

2022. 12. 7. 16:39_Study/AndroidStudio

728x90

13-4 태스크 관리 🐇¸.•*¨*•¸.•*¨*•¸.•*¨*•¸.•*¨*•

해당 자료는 강의 학습자료이며, Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린을 참고하였습니다.


 

시스템에서 태스크 관리

액티비티 태스크란 앱이 실행될 때 시스템에서 액티비티의 각종 정보를 저장하는 공간으로 사용자가 기기의 뒤로가기 버튼을 누르면 이 태스크에서 위쪽에 있는 액티비티를 종료된다. (stack)

 

앱과 앱이 연동되어 실행되는 구조

앱 태스크에는 다른 앱의 activity가 쌓일 수 있다. 실행되면 위에 쌓이는 stack 구조

강의 자료 입니다.

태스크 제어

 

태스크를 제어하는 2가지 방법으로 액티비티가 등록되는 매니페스트 파일의 <activity> 태그의 launchMode 를 이용하거나 인텐트의 flags 정보를 설정하여 제어한다.

 

<activity> 태그의 launchMode 속성으로 실행 모드를 설정하거나

코드에서는 인텐트를 발생시키기 전에 인텐트의 flags 속성에 설정한다.

val intent = Intent(this,SettingActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)

스탠더드로 설정 (기본값)
- 인텐트가 발생하면 항상 객체가 생성되고 태스크에 등록 ( 두 개가 쌓인다 )

강의 자료 입니다

싱글 톱으로 설정
- 액티비티 정보가 태스크의 위쪽에 있을 때 인텐트가 발생해도 객체를 생성하지 않는다.
- 기존 객체의 onNewIntent () 함수가 자동으로 호출

강의 자료 입니다.

 

 

싱글 태스크로 설정
singleTask 로 설정하면 새로운 태스크를 만들어 등록
single Task 설정은 같은 앱에서는 적용되지 않으며 다른 앱의 액티비티를 인텐트로 실행할 때에만 적용

 

 

싱글 인스턴스로 설정

singleInstance 로 설정하면 싱글 태스크처럼 새로운 태스크를 만들어 등록하는데 , 그 태스크에는 해당 설정이 적용된 액티비티 하나만 등록한다.

 

 

 

13-5 액티비티 ANR 문제와 코루틴 🐇¸.•*¨*•¸.•*¨*•¸.•*¨*•¸.•*¨*•


ANR 문제란?

ANR은 액티비티가 응답하지 않는 오류 상황 -> 너무 요청시간이 길어서 등...
 시스템에서 액티비티를 실행하는 수행 흐름을 메인 스레드 또는 화면을 출력하는 수행 흐름이라는 의미에서 UI 스레드라고 한다.

 

ANR 문제를 해결하는 방법은 액티비티를 실행한 메인 스레드 이외에 실행 흐름 개발자 스레드 을 따로 만들어서 시
간이 오래 걸리는 작업을 담당하게 하면 되지만 이 방법으로 대처하면 ANR 오류는 해결되지만 화면을 변경할 수 없다는 또 다른 문제가 발생한다.

 

 

 

 

코루틴으로 ANR 오류 해결

비동기 경량 스레드
프로그래밍 언어에서 제공하는 기능
코루틴을 사용하라고 권하면서 다음과 같은 장점이 있다고 소개한다.
- 경량
- 메모리 누수가 적음
- 취소 등 다양한 기능을 지원함
- 많은 제트팩 라이브러리에 적용되어 있음

 

안드로이드에서 코루틴 이용 build.gradle에 추가하고 sync

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"

강의 버전은 돌아가지 않는 것 같아서 버전 바꿈

 

 

ANR 문제를 발생시키기 위한 오래걸리는 작업 (사양에 따라 시간을 늘리면 된다)

var sum = 0L
    var time = measureTimeMillis {
        for(i in 1..2_000_000_000){
            sum += i
    }}
    Log.d("kkang", "time : $time")
    binding.resultView.text = "sum : $sum"
}

발생 확인

 

 

ANR 오류를 해결하고자 스레드 핸들러 구조로 다시 작성

val handler = object :Handler(){
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        binding.resultView.text = "sum : ${msg.arg1}"
    }
}
thread {
//binding.LongTimeForANR.setOnClickListener {
    var sum = 0L
    var time = measureTimeMillis {
        for (i in 1..2_000_000_000_000) {
            sum += i
        }
        val message = Message()
        message.arg1 - sum.toInt()
        handler.sendMessage(message)
    }
    Log.d("kkang", "time : $time")
//}
}

 

코드를 코루틴으로 작성
코루틴을 구동하려면 먼저 스코프를 준비하고 스코프에서 코루틴을 구동한다.
코루틴 스코프: CoroutineScope 를 구현한 클래스의 객체
직접 구현할 수도 있고 Global Scope, ActorScope , ProducerScope 등 코틀린 언어가 제공하는 스코프를 이용할 수도
있다. 백그라운드에서 작동한다.

val channel = Channel<Int>()
val backgroundScope = CoroutineScope(Dispatchers.Default + Job())
backgroundScope.launch {
    var sum = 0L
    var time = measureTimeMillis {
        for (i in 1..2_000_000_000_000) {
            sum += i
        }
        val message = Message()
        message.arg1 - sum.toInt()
    }
    Log.d("kkang", "time : $time")
    channel.send(sum.toInt())
}
val mainScope = GlobalScope.launch(Dispatchers.Main) {
    channel.consumeEach {
        binding.resultView.text = "sum : $it"
    }
}

 

 

디스패처는 이 스코프에서 구동한 코루틴이 어디에서 동작해야 하는지를 나타낸다.
- Dispatchers.Main : 액티비티의 메인 스레드에서 동작하는 코루틴을 만든다.
- Dispatchers.IO: 파일에 읽거나 쓰기 또는 네트워크 작업 등에 최적화되어있다.
- Dispatchers.Default : CPU 를 많이 사용하는 작업을 백그라운드에서 실행한다.