해당 포스트는 "안드로이드 비동기 프로그래밍" 책의 내용을 요약한 것이다.



※ 달빅 가상 머신(DVM)

: 안드로이드는 JVM을 사용하지 않고 달빅 가상 머신(DVM)을 사용한다. 따라서 자바 소스를 자바 바이트 코드로 컴파일 한 후 DVM 위에서 실행하는 달빅 실행 파일(Dalvic execute file)로 변환한다. JVM을 사용하지 않고 DVM을 사용하는 이유는 JVM이 데스크톱/서버 환경에 최적화 되 있기 때문이다. DVM은 메모리/프로세서/전력이 제환된 안드로이드 환경에 맞게 설계되었다. 


자이고트

: 안드로이드는 초기에 부팅될 때 자이코트(Zygote) 프로세스를 실행한다. 자이고트는 가상 머신을 구동하고 코어 라이브러리를 적재하고 다양한 공유 구조체를 초기화한다. 그런 후 소켓에 연결 대기를 함으로써 명령을 기다린다. 만약 어플을 실행하면 자이고트가 미리 가지고 있던 가상 머신 프로세스를 생성하고 Copy-On-Write 기술을 사용해 메모리를 부모와 공유하는 새로운 자식 프로세스를 생성한다. 이렇게 하면 가상 머신과 코어 라이브러리가 미리 적재되 있기 때문에 가상 머신 초기화를 위해 많은 데이터 양을 읽지 않아도 되 구동 과부하를 줄여준다. 또한 코어 라이브러리와 공용 구조체가 있는 메모리를 자이고트 및 모든 앱과 함께 공유하기 때문에 사용자가 여러 앱을 실행할 때 많은 메모리를 아낄 수 있다.


※ 스레드

- 메인 스레드

: 안드로이드 애플리케이션을 구축할 때 메인 스레드를 봉쇄하면 안 된다. 메인 스레드에서 복잡한 계산, 파일 읽기/쓰기, 네트워킹 같은 작업을 하면 스크롤이나 터치와 같은 사용자 입력이 즉시 반응할 수 없다. 사용자는 200ms 이상의 지연이 느껴지면 알아채린다. 또한 앱이 사용자 응답에 대해 5초 이내에 응답하지 않으면 ANR(Application Not Response) 가 된다. 안드로이드는 화면 다시 기르기를 초당 60 프레임, 즉 프레임당 16.67밀리초를 목표로 한다. 따라서 메인 스레드에서 16밀리초 가깝게 작업한다면 버벅거리고 렉 걸리는 결과로 이어질 것이다. 


따라서 사용자와의 반응성이 유지되야 한다. 오래 걸리는 작업을 백그라운드에서 하고 메인 스레드에서 사용자 인터페이스 처리를 하되 백그라운드는 사용자와 용의한 상호작용을 위해 적절한 시간에 응답해야 한다. 또한 백그라운드와 스레드 사이에 안전하게 데이터 전달이 가능해야 하고 많은 백그라운드 작업을 동시에 수행하기 위해 여러 개의 CPU를 잘 활용해야 한다.


- 멀티 스레드

안드로이드에서 멀티 스레드를 지원하기 위한 매커니즘으로는 다음과 같이 있다. Thread, Runnable, synchronized, volatile, Executors, 원자적 래퍼 클래스(atomic wrapper class), 생성자 잠금(locking construct), 동시성 컬렉션(concurrent collection)이 있다. 멀티 스레드를 다루는 데 중요한 요소는 정확성(일관적이되 정확한 결과를 만듬), 활동성(완료를 향향 전진)이다.


a. 정확성

: 두 스레드가 a 변수에 접근해서 1을 더하는 연산을 한다고 가정하자. 정상적으로 실행이 되면 첫 번째 스레드가 a에 1을 더하고 두 번째 스레드가 a+1에 1을 더하기 때문에 a는 a+2가 되야 된다. 하지만 첫 번째 스레드가 a에 1을 더할려고 a값을 읽고 더하기 직전에 두 번째 스레드가 a의 값을 읽는다. 결국에는 첫 번째 스레드 결과로 a+1이 되고 두 번째 스레드 결과도 a+1이 되 정확한 값이 안 나오게 된다. 이러한 상황을 경쟁 조건이라고 한다. 이에 대한 해결책은 특정 자원에 동시 접근이 불가능 하도록 배제 락을 획득함으로써 상호 배제하는 것이다. 


b. 활동성

: 활동성 문제는 정확성 문제를 해결하기 위해 나온 락으로 인한 부작용이라고 말할 수 있다. 하나의 자원에 접근하는 여러 스레드가 있는 데 락을 잡는 하나의 스레드만 자원에 접근 가능하다. 따라서 락을 잡지 못한 나머지 스레드는 락을 잡은 스레드가 작업을 완료될 때까지 기다려야 한다. 즉, 병목 현상이 만들어지고 지연이 발생한다. 여기서 심각한 상황이 발생할 수도 있다. 만약 락을 잡은 스레드가 락을 놓지 않으면 다른 스레드는 해당 락을 얻을 때까지 기다리게 될 수도 있다. 이런 상황을 데드락(Deadlock)라고 한다.


안드로이드에는 스레드에 대한 추가적인 문제가 있다. 
a. 액티비티 생명주기
액티비티 인스턴스가 생명주기가 끝나면 가비지 컬렉션 대상이 되는 데 해당 액티비티나 뷰 계층과 관련된 백그라운드 스레드는 가비지 컬렉션을 피하거나 메모리 누수를 일으킬 수 있다. 또한, 안드로이드는 사용자가 프로세스를 언제든지 종료할 수 있기 때문에 완료하기 전까지 오래 걸리는 작업이 있다면 프로세스를 아직 종료하지 않음을 시스템에게 알려주는 방법도 필요하다. 따라서 메인 스레드의 미봉쇄  규칙에 따라 액티비티 생명주기의 적절한 곳에 이러한 백그라운드 처리 작업을 분리시켜야 한다.

b. 사용자 인터페이스 조작
: 안드로이드는 메인 스레드가 아닌 어떠한 스레드에서 사용자 인터페이스 조작이 불가능하다. 따라서 액티비티 컴포넌트에 대해 여러 스레드가 접근해서 나오는 위험성을 없애준다. 대신 메인 스레드가 백그라운드 작업 결과와 함께 UI를 갱신하도록 백그라운드 스레드를 메인 스레드와 함께 안전하게 동기화해야 한다.

안드로이드에서는 위에서 설명한 문제들에 관해서 해결해주는 특별한 생성자가 존재한다. 차근차근 그에 대해서 알아볼 예정이다.



+ Recent posts