[이 포스트는 Dianne Hackborn 에 의해 작성되었습니다. 그녀는 안드로이드의 개발에 매우 중요한 위치에 있는 소프트웨어 엔지니어 입니다. - Tim Bray]
Multitasking the Android Way
Design considerations
디자인 고려사항
모바일 디바이스는 기술적인 한계점을 갖으며 일반적인 데스크탑 혹은 웹환경과는 다른 사용자 경험이 요구됩니다. 다음은 안드로이드의 멀티태스킹 기능을 설계하는데 우리가 중요하게 여긴 네가지 제약 사항들 입니다.
- 우리는 사용자에게 특정 어플리케이션 사용을 끝마쳤을 때, 명시적으로 어플리케이션을 종료하지 않아도 되기를 원했다. 사용자들은 다양한 종류의 어플리케이션을 아쭈 짧게 그리고 반복적으로 사용하기 때문이다.
- 휴대용 장치들은 풍족한 스왑 메모리 공간을 갖고 있지 못하며, 메모리 사용에 빡빡한 제한을 갖고 있다. Robert Love 가 작성한 이 주제를 다루는 매우 좋은 글이 있다.
- 모바일 디바이스에서 어플리케이션 스위칭은 중요하다. 우리는 새로운 어플리케이션이 1초 이내에 시작될 수 있도록 굉장한 노력을 기울였다. 비디오를 보다가 새로 도착한 SMS 문자를 확인하고 다시 비디오 플레이 어플리케이션으로 돌아가는 등의 사용자 시나리오를 생각해 본다면, 몇몇 어플리케이션간의 빠른 스위칭은 특히 더 중요한 문제이다.
- 사용할 수 있는 API 는 안드로이드에 기본 내장된 구글 어플리케이션을 만들 수 있을 만큼 충분히 강력해야 한다. 이것은 우리의 '모든 어플리케이션은 동등하다,' 라는 철학의 문제이다. 즉, 백그라운드 음악 재생, 데이타 동기화, GPS 네비게이션, 어플리케이션 다운로드등의 기능은 서드 파티 어플리케이션 개발자들이 사용가능한 API 와 동일한 API 를 이용해서 작성되어야 한다.
앞선 두가지 요구사항은 한가지 흥미로운 모순점을 갖고 있습니다. 우리는 사용자들이 어플리케이션 종료에 대해 신경쓰는 것 대신 "모든 어플리케이션은 항상 실행 중"인 것처럼 느낄 수 있기를 바랍니다. 하지만 동시에 모바일 디바이스는 메모리 사용에 빡빡한 제한이 있습니다. 시스템이 사용 가능한 램 영역 보다 많은 메모리가 필요한 경우가 되면, 데스크탑 컴퓨터들은 단순히 페이지 스왑을 위해 작동이 조금 느려지게 됩니다. 반면, 모바일 디바이스는 성능상에 큰 문제점이 생기거나 아예 먹통이 되버릴 수 있습니다. 이 도전할만한 제약 조건이 안드로이드의 멀티태스킹 메커니즘을 설계하기위한 핵심 동기가 되었습니다.
When does an application "stop"?
어플리케이션은 언제 '정지' 되지?
안드로이드의 멀티태스킹과 관련된 가장 일반적인 오해는 프로세스와 어플리케이션의 차이에 관한 점입니다. 안드로이드에서 프로세스와 어플리케이션은 밀접하게 연결(tightly coupled)되어 있는 요소가 아니며, 어플리케이션은 해당 어플리케이션을 작동시키고 있는 프로세스가 없음에도 사용자에게 현재 작동하고 있는 것 처럼 보일 수도 있고, 또, 여러 어플리케이션이 프로세스를 공유 할 수도 있습니다. 혹은 필요에 따라 하나의 어플리케이션이 여러개의 프로세스를 사용할 수도 있고, 실재로 어플리케이션이 작동 중이 아님에도, 어플리케이션을 구동했던 프로세스들은 종료되지 않고 안드로이드 시스템에 의해 유지될 수 있습니다.
즉, 개발자가 어플리케이션 프로세스가 'Running' 상태임을 확인 할 수 있다고 해서, 그것이 해당 어플리케이션이 현재 작동중이거나 어떠한 일을 하고 있다는 것을 의미하지 않습니다. 어플리케이션의 프로세스는 단순히 안드로이드 시스템이 해당 프로세스가 필요 했었고, 이 후에 해당 프로세스가 다시 사용될 경우에 대비해서 프로세스를 유지하기로 결정했기 때문에 살아있는 것 뿐입니다. 이와 비슷하게, 개발자가 아주 잠시동안 특정 어플리케이션을 떠나 곧 돌아 오기를 원하고자 하는 경우라할지라도, 해당 어플리케이션을 작동시키고 있는 프로세스는 안드로이드 시스템에 의해 강제로 종료될 수 있습니다.
사실, 안드로이드가 어플리케이션을 관리하는 핵심은 프로세스를 깔끔하게 종료시키지 않는 것입니다. 사용자가 어플리케이션을 떠나는 순간, 해당 어플리케이션의 프로세스는 계속 유지되며, 백그라운드 상에서 필요한 경우 어떠한 작업(예를 들어 웹페이지 다운로드와 같은)을 수행할 수 있습니다. 그리고, 사용자가 해당 어플리케이션으로 돌아오면 그 즉시 포그라운드로 전환됩니다. 만일 디바이스의 메모리가 충분하다면, 안드로이드는 모든 어플리케이션 프로세스들을 유지하게 되고, 말 그대로 모든 어플리케이션은 동시에 작동(Running) 할 수 있습니다.
물론, 메모리는 무한하지 않습니다. 이러한 한계를 극복하기 위해, 안드로이드 시스템은 더이상 필요하지 않은 프로세스를 종료해야만 합니다. 즉, 각각의 프로세스들은 정해진 규칙에 따라 그 중요도가 결정되고, 가장 중요하지 않은 프로세스가 종료되게 됩니다. 이러한 과정이 안드로이드의 '프로세스 생명주기' (Process Lifecycle) 를 만들어 냅니다. 프로세스의 중요도는 프로세스가 현재 사용자에게 얼마나 큰 영향을 미치고 있는지, 그리고 사용자가 마지막으로 해당 프로세스를 필요로 한 것이 얼마나 오래전인지 두 가지 요소에 의해 결정됩니다.
안드로이드 시스템이 특정 프로세스를 제거하기로 결정하면, 강제로 프로세스를 종료(force close) 합니다. 따라서, 커널은 해당 프로세스가 시스템의 프로세스 종료 요청에 대하여 사용중인 자원을 적절하게 반환하도록 작성되었는지 그렇지 않은지와 관계없이, 즉시 해당 프로세스에 의해 사용중이던 리소스들을 사용할 수 있습니다. 커널이 어플리케이션이 사용중이던 리소스를 즉시 사용할 수 있도록 함으로서, 시스템 메모리 부족으로 인해 발생하는 심각한 문제들을 훨씬 쉽게 회피할 수 있게 됩니다.
'모든 어플리케이션은 항상 작동하고 있다' 라는 사용자 경험을 만족시키기 위해, 만일 사용자가 이미 종료된 어플리케이션으로 돌아가기를 원하는 경우, 해당 어플리케이션의 마지막 상황과 동일한 형태로 어플리케이션이 시작되어야 합니다. 이를 위해, 사용자에게 보여지는 어플리케이션 요소 (즉, Activity)은 늘 기록 되며, 필요한 경우 특정 Activity가 화면상에 보였던 상태 정보와 함께 Activity 를 재시작 합니다. Activity의 상태 정보는 어플리케이션 종료 시점이 아니라, 사용자가 어플리케이션을 떠날 때 마다 생성됨으로, 커널은 사용자가 어플리케이션을 벗어난 후에는 비교적 자유롭게 해당 어플리케이션을 종료 할 수 있습니다.
어떤면으로 보면, 안드로이드가 프로세스를 관리하는 방법은 공간 교체(swap space)의 형식으로 여겨질 수 있습니다. 어플리케이션 프로세스들은 사용중인 메모리 공간을 나타냅니다. 메모리가 부족하게 될 경우 몇몇 프로세스들은 강제로 종료 되고(교체 당함 - Swapped Out) ,해당 프로세스들이 다시 필요하게 될 경우 마지막으로 저장된 상태 정보를 기반으로 다시 시작(교체 투입 - Swapped In)될 수 있습니다.
Explicitly running in the background
명시적으로 백그라운드에서 작업하기
어플리케이션 프로세스가 안드로이드 시스템에 의해 강제로 종료되지 않는 한, 해당 암시적으로 백그라운드 작업을 수행할 수 있습니다. 하지만, 이런 기능이 웹페이지를 로딩하는 등의 일을 하기에는 충분할지 모르지만, 예를 들어 백그라운드에서 음악을 재생, 데이타를 동기화, 위치 정보를 기록, 알람 등과 같이보다 엄밀한 요구사항이 필요한 경우에는 적절하지 못합니다.
이러한 작업들을 위해서, 어플리케이션은 안드로이드 시스템에게 명시적으로 백그라운드 상에서 작업이 수행되어야 함을 알릴 필요가 있습니다. 어플리케이션은 메니페스트 상에 'Broadcast Receiver' 혹은 'Service' 요소를 선언할 수 있으며, 이 두가지 요소를 통해 명시적으로 백그라운드 작업을 수행 할 수 있습니다.
Broadcast Receivers
Broadcast Receiver 는 어플리케이션이 특정한 이벤트가 발생하는 경우에, 아주 짧은 시간동안 백그라운드에서 작업할 수 있도록 해주며, 다양한 방식으로 보다 상위에 기능을 구현하는데 사용 될 수 있습니다. 예를 들어 AlarmManager 는 어플리케이션이 미래의 특정 시점에 Broadcast 를 전송 할 수 있도록 해주며, LocationManager 는 위치 정보가 변경될 때마다 Broadcast 를 전송할 수 있습니다. BroadcastReceiver 에 관한 정보는 어플리케이션 메니페스트에 포함되기 때문에, 안드로이드 시스템은 현재 작동 하지 않는 어플리케이션의 BroadcastReceiver 를 찾아서 실행 시켜 줄 수 있으며, 물론, 어플리케이션이 현재 작동 중이라면, 그 어플리케이션에 속하는 BroadcastReceiver 는 매우 효율적으로 실행 됩니다.
Broadcast 를 처리하기 위해 어플리케이션은 최대 10초의 시간만을 사용할 수 있있습니다. 만일 해당 시간내에 작업을 완료하지 못하면, 그 어플리케이션은 오작동하는 것으로 간주되고, 그 즉시 해당 어플리케이션 프로세스는 메모리 확보를 위해 필요한 경우 언제든지 종료될 수 있는 상태(backgroud state - 가장 낮은 중요도를 갖는 프로세스)로 전환됩니다.
BroadcastReceiver 는 GPS 위치 정보가 변경되었음을 사용자에게 알리는 일과 같이 외부의 자극에 대해 반응하는 아주 간단한 일을 수행하는데 매우 훌륭하며, Broadcast 를 실재로 수신하는 동안에만 어플리케이션 프로세스가 작동하기 때문에 매우 가볍습니다. 또한, 정해진 시간 동안만 활성화 되기 때문에 실재로 작동하는 10초간은 해당 프로세스가 죽지 않을 것이라는 것을 매우 강력하게 보장해 줍니다. (주>따라서 갑작스런 프로세스 종료에 대한 예외사항을 고려하지 않아도 된다.) 그러나 네트워크 관련 일과 같은 비교적 긴 시간이 필요한 작업을 수행하는데는 적합하지 않습니다.
Services
Service는 어플리케이션이 보다 긴 백그라운드 작업을 수행할 수 있도록 해줍니다. Service 는 여러가지 기능이 있지만, 근본적인 역할은, 어플리케이션이 안드로이드 시스템에게 "헤이~ 나는 내가 종료되었다고 하기 전까지 백그라운드에서 계속해서 작동했으면 좋겠어!" 라고 알려주는 것입니다. 어플리케이션은 명시적으로 Service 를 시작 하거나 종료 시킬 수 있습니다.
Service 는 풍부한 클라이언트-서버 모델에 관련된 기능을 제공해 주지만, 어디까지나 옵션에 불과합니다. 어플리케이션에 속한 Service 를 시작하는데 있어서, 안드로이드 시스템은 단순히 해당 Context 를 갖는 어플리케이션 요소 하나를 생성할 뿐입니다. 이 후에 Service 와 어떻게 상호작용 할 것인지는 전적으로 어플리케이션에 달린 문제입니다. Service 내부에 필요한 모든 코드를 작성한 후 어플리케이션의 다른 요소들과는 전혀 별개로 작동해도 좋고, 어플리케이션 내에 모든 요소들이 공유하는 Singleton 객체를 생성한 후 해당 Singleton 객체의 메서드를 호출해도 좋습니다. 혹은 어플리케이션의 다른 쪽에서 생성된 Service 객체를 직접 참조하여 사용해도 좋고(Local Service), Service 를 별도의 프로세스에서 작동하게 한 후에 RPC 프로토콜을 통해 어플리케이션의 나머지 요소들과 상호작용 할 수도 있습니다(Remote Service).
작동중인 Service 의 수와 각각의 Service 가 작업을 완료하는데 걸리는 시간에는 정해진 한계가 없기 때문에, Service 가 작동 중인 프로세스를 관리하는 것은 BroadcastReceiver 의 경우와는 차이점이 있습니다. 요청된 모든 Service 들을 동시에 작동하는데는 메모리가 부족할 수 있으며, 때문에 Service 들이 항상 작동한다는 것을 확실히 보장할 수는 없습니다.
만일 메모리가 부족해 진다면, Service 가 작동중인 프로세스 또한 강제로 종료되어야 합니다. 하지만, 만일 강제로 종료된 Service 들이 계속해서 작동하고자 한다면, 안드로이드 시스템은 이후에 메모리에 여유가 생길 때, 종료된 프로세스를 다시 시작하여 Service 를 재실행 시켜줍니다. 예를 들어 사용자가 굉장히 큰 메모리 용량을 요구하는 웹페이지에 갔다면, 안드로이드 시스템은 브라우저가 필요로 하는 메모리 공간을 확보하기 위해, 데이타 싱크 작업을 수행하는 프로세스를 종료할 수 있습니다.
Service 는 안드로이드 시스템에 미리 자신을 'Foreground' 상태로 간주해 달라고 이야기할 수도 있습니다. 이것은, "나를 죽이지 마세요" 라는 뜻인데, 이 경우 해당 Service 는 사용자에게 자신이 현재 작동중임을 Notification (안드로이드 화면 상단의 상태바) 을 통해 표시해 주어야 합니다. 백그라운드 음악 플레이 혹은 자동차 네비게이션등과 같은 Service 에 유용한데, 사용자들은 웹브라우징을 할 때, 상태바의 음악 재생 아이콘을 보고, 현재 음악이 재생 중임을 알 수 있습니다. 안드로이드 시스템은 'Foreground' 로 간주되길 원하는 Service 들을 최대한 종료시키지 않도록 노력하며, 그와 동시에, 사용자들이 해당 Service 들이 현재 작동중임을 알 수 있고, 필요한 경우 명시적으로 종료 할 수 있도록 보장합니다.
The value of generic components
기본구성 요소(Service 와 BroadcastReceiver)의 가치
개발자들은 안드로이드의 BroadcastReceiver 와 Service 를 이용하여, 다양한 형태의 백그라운드 작업을 효율적으로 수행할 수 있습니다. 안드로이드 1.0 에서 구글 어플리케이션의 거의 모든 백그라운드 작업을 구현하는데 이 요소들이 사용되었습니다.
- Service 에서 음악을 재생하는 것은 사용자가 음악 어플리케이션을 떠난 후에도 음악이 계속 재생되도록 한다.
- 알람시계는 Alarm Manager 를 이용하여, 다음 정해신 알람 시간까지는 작동하지 않는 BroadcastReceiver 로 구현되어 있다.
- 달력 어플리케이션도 유사하게, 다음 정의된 이벤트때 알림내용을 표시하거나 갱신하기 위해 AlaramManager 를 통해 알람을 설정한다.
- 백그라운드 파일 다운로드 기능은 진행중인 다운로드가 있는 경우 작동하는 Service 로 구현되었다.
- E-Mail 어플리케이션은 정해진 시간마다 새로운 메일을 확인하기 위한 Service 를 작동시키도록 알람을 설정한다.
- 구글 어플리케이션들은 네트워크를 통한 Push 이벤트 수신을 위한 Service 를 유지하며, 특정 Push 이벤트에 의해 주소록 동기화 같은 작업을 수행할 필요가 있을 경우, 각 어플리케이션에 Broadcast 를 전달한다.
플랫폼이 진화함에 따라, 이 기본 요소들은 다양한 추가적인 중요 기능을 구현하는데 사용되었습니다.
- Input Method 는 안드로이드 시스템이 관리하며, 현재 IME 설정에 따라 화면에 Input Method 를 보여 주도록 Service 로 개발되었다.
- 어플리케이션 위젯들은 BroadcastReceiver 이고, 안드로이드 시스템이 위젯과 상호작용하고자 할 경우 Broadcast 를 전달해야 한다. 따라서 어플리케이션 웨젯들 해당 프로세스가 항상 작동중이 아니어도 되기 때문에 꽤 가볍다.
- 접근성 기능 (TTS 와 같은)들은 작동중에 안드로이드 시스템이 유지하고 적절한 사용자 인터렉션 정보를 전달하는 Service 로 구현되어 있다.
- 안드로이드 2.0 에서 소개된 Sync Adapter 는 특정한 데이타 싱크 작업이 수행되어야 할 경우 백그라운드에서 작동하는 Service 이다.
- Live Wallpaper 는 사용자 선택에 의해 안드로이드 시스템이 시작하는 Service 이다.