안드로이드 멀티스레딩 책을 공부하며, 필요한 내용을 정리한다. 멀티스레딩 공부에 이 자료가 유용히 사용되었으면 좋겠다. 이 자료에서의 응용프로그램은 앱과 동일한 표현이라고 보면 된다.
안드로이드 소프트웨어 스텍
안드로이드의 응용프로그램은 리눅스 커널, 네이티브 C++라이브러리 그리고 코드를 실행하는 런타임을 기반으로 하는 소프트웨어 스택위에서 동작한다.
리눅스 커널
응용프로그램에서 소리, 네트워크, 카메라 등 기기(device)의 하드웨어 기능을 사용하기위한 하부 운영체제. 프로세스와 스레드도 관리. 커널은 스케줄링을 통해 프로세스와 스레드가 사용하는 CPU 실행 시간을 나눔.
네이티브 라이브러리
그래픽, 미디어, 데이터베이스, 폰트, OpenGL 등을 다루는 C/C++ 라이브러리. 안드로이드 프레임 워크는 네이티브 코드를 위한 자바 wrapper를 제공하기 때문에 응용프로그램은 기본적으로 네이티브 라이브러리와 직접 상호작용하지않음
런타임
내부 바이트 코드 표현으로 가상 머신에서 컴파일된 안드로이드 응용프로그램 코드를 실행하는 런타임 환경이다. 롤리팝 버전 이전에는 달빅이 기본이었으나, 롤리팝 이후에는 Android Runtime(ART)이 새로나와서 달빅이 가졌던 문제를 개선했다(1 배터리 문제, 2 램문제)
응용프로그램 아키텍처
응용프로그램은 Application 객체, 안드로이드 구성요소(Activity, Service, BroadcastReceiver, ContentProvider)로 구성되어있다
Application 객체
이 객체는 응용프로그램이 시작될 때 인스턴스화되고, 중지될 때 소멸한다. 이 말은 즉, Application 클래스의 인스턴스는 응용프로그램이 리눅스 프로세스 수명동안만 지속된다는 의미이다. 프로세스가 종료되고 재시작할 때 새로운 Application 인스턴스가 생성된다
안드로이드 구성요소
각 구성요소는 서로 다른 책임과 생명주기를 갖지만, 모두 응용프로그램을 시작할 수 있는 진입점이 된다. 구성요소가 시작되면 응용프로그램의 생명주기를 통해 다른 구성요소들을 시작시킬 수 있다(인텐트를 사용)
명시적 인텐트
컴파일 시 응용프로그램에서 알 수 있는 구성요소의 이름을 분명하게 나타냄
암시적 인텐트
IntentFilter 안에 여러개의 특성으로 정의한 구성요소에 런타임 바인딩. 만약 해당 인텐트가 어느 구성요소의 IntentFilter 특성과 일치하며 그 구성요소가 시작될 수 있음
응용프로그램의 모든 구성요소는 AndroidManifest.xml 파일에 등록되어야 한다
- Activity
액티비티는 사용자에게 보이는 화면. 정보를 표시하거나 사용자의 입력 등을 담당. UI구성요소(버튼, 텍스트, 그림 등)를 포함. 뷰 계층 구조에 대한 객체 참조를 담고 있다(메모리가 차지하는 공간이 매우 커질 수 있음).
액티비티의 상태는 응용프로그램의 종료 가능성과 스레드의 스케줄된 실행 시간에 영향을 주는 시스템 우선순위에 영향을 미침
- Service
서비스는 사용자의 직접 상호작용 없이 백그라운드에서 실행 한다. 일반적으로 동작이 자신의 수명보다 오래 살 수 있을 때, 다른 구성요소에 싱행을 떠넘기기 위해 사용. 시작 또는 바운드 모드로 실행됨
시작 서비스
Context.startService(Intent) — 시작
Context.stopService(Intent) — 종료
바운드 서비스
Context.bindService(Intent, ServiceConnection, int) — 여러 구성요소를 서비스에 바인딩. ServiceConnection 인터페이스를 통해 그 서비스와 상호작용
Context.unbindService(Intent, ServiceConnection, int) — 서비스로부터 바인딩 해제. 마지막 구성요소가 서비스로부터 바인딩 해제되면 서비스는 소멸
- BroadcastReceiver
응용프로그램 내, 원격 응용프로그램 또는 플랫폼으로부터 보내진 인텐트를 듣는 기능을 가진 구성요소. 어떤 인테트가 브로드캐스트 리시버로 보내진 것인지 가리기 위해 수신되는 인텐트를 필터링. 인텐트 수신을 시작하려면 브로드캐스트 리시버를 동적으로 등록해야함. 수신을 종료하려면 등록을 해제해야함. AndroidManifest.xml에 정적으로 등록되어있다면 응용프로그램이 설치되어 있는 동안 인텐트를 수신할 수 있다(어떤 인텐트가 필터에 일치하면 브로드캐스트 리시버는 연결된 응용프로그램을 시작할 수 있다)
- ContentProvider
응용프로그램 내부 혹은 응용프로그램 사이에서 많은 양의 데이터를 공유할 때 사용되는 구성요소.
응용프로그램의 실행
안드로이드는 동시에 여러개의 응용프로그램을 실행할 수 있고, 사용자 전환이 가능한 멀티유저, 멀티태스킹 시스템이다. 리눅스 커널은 멀티태스킹을 처리하고, 응용프로그램 실행은 리눅스 프로세스를 기반으로 한다.
리눅스 프로세스
안드로이드에서 모든 응용프로그램 패키지는 고유한 사용자 ID를 가짐(리눅스의 특성). 이러한 특성 때문에 다른 응용프로그램의 리소스에 액세스 할 수 없다. 일반적으로 리눅스 프로세스 위에 런타임 위에 앱이 있는 형식이다.
생명주기
런타임이 onCreate() 함수를 호출할 때 각 응용프로그램에 대한 Application 객체가 시작된다. Application 객체는 프로세스에 첫번째로 생성되는 구성요소이자 마지막으로 소멸되는 구성요소다.
응용프로그램 시작
- 리눅스 프로세스 시작
- 런타임 생성
- Application 인스턴스 생성
- 응용프로그램을 위한 진입점 구성요소 생성
Zygote(프로세스)는 미리 로드된 핵심 라이브러리의 전체 세트를 가지고 있어, 새로운 응용프로그램 프로세스는 모든 응용프로그램에서 공유되는 핵심 라이브러리를 복사하지 않고 zygote 프로세스에서 포크한다.
응용 프로그램 종료
프로세스는 응용프로그램이 시작될 때 생성되며 시스템 자원이 해제될 때 종료된다. 사용자가 나중에 응용프로그램을 재실행할 수 있기 때문에 런타임은 시스템 전체 자원이 부족해지기 전까지는 현재 살아 있는 응용프로그램의 모든 자원을 소멸시키지 않음. 따라서 모든 구성요소가 소멸해도 응용프로그램이 자동적으로 종료되지 않음.
시스템 자원이 부족할 때 런타임과 프로세스 우선순위에 따라서 프로세스가 종료된다.
프로세스 순위가 높은 순서
- 포그라운드 — 전면에 보이는 구성요소를 가진 응용프로그램, 원격 프로세스에서 전면의 애기비티에 바인딩된 서비스, 실행 중인 브로드캐스트 리시버
- 화면에 보이는 프로세스 — 화면에는 보이나 부분적으로 숨겨진 구성요소를 가진 응용프로그램
- 백그라운드 — 화면에 보이지 않는 액티비티(대부분의 응용프로그램이 가지는 프로세스 레벨)
- 빈 프로세스 — 활성화된 구성요소가 없는 프로세스
성능을 위해 구조화된 응용프로그램
높은 처리량과 더 나은 성능을 만들기 위해, 응용프로그램은 각각의 프로세스 내에서 여러 스레드를 사용해야 한다.
스레드를 통해 반응성 있는 응용프로그램 만들기
응용프로그램은 여러 개의 CPU에서 높은 처리량을 가진 비동기 실행이 가능하지만, 반응성 있는 응용프로그램을 보증하지 않는다. 반응성은 상호작용하는 동안 사용자가 응용프로그램을 인식하는 방식을 말한다(UI가 버튼 클릭에 신속하게 반응하거나 부드러운 애니메이션 등). UI 구성요소를 업데이트 하는 역할은 UI스레드(메인스레드)가 유일하게 가지고 있다.
따라서 응용프로그램을 반응성 있게 만드려면, 오래걸리는 태스크를 UI스레드에서 실행하면 안 된다. 일반적으로 5~10초 동안 UI스레드를 지연할 때 런타임은 ANR 대화상자를 보여준다. 따라서 긴 작업은 백그라운드 스레드에서 처리되어야 한다.
오래 걸리는 태스크 목록
- 네트워크 통신
- 파일 읽기/쓰기
- 데이터베이스 생성/삭제/수정
- SharedPreferences 읽기/쓰기
- 이미지 프로세싱
- 텍스트 파싱
일반적으로 애니메이션 기준으로 추론할 경우, 초당 60프레임이 안되면 사용자가 인식할 수 있다고 한다. 따라서 모든 태스크는 16ms 이내에 실행되어야 한다(대략적으로)
마치며
오늘은 안드로이드 멀티스레딩 1장의 내용을 요약해 보았다. 안드로이드 앱이 어떤 기반에서 실행되는지, 어떤 구성요소를 가지고 있는지, 어떻게 실행되고 어떻게 종료되는지, 더 좋은 반응성을 가진 앱을 만들기 위해서 왜 스레드를 사용하는지에 대해서 정리해 보았다.
