[Android] Activity 살펴보기 : Activity 의 이해
Activity 의 이해
우리가 안드로이드 앱을 개발하기 위해서는 흔히 4대 component 라고 불리는 Activity, Service, Broadcast Receiver, Contents Provider 가 필요합니다. 그 중에서 특히 Activity 를 제외한 나머지 컴포넌트들은 요구사항에 따라 추가하면 되지만 Activity 는 반드시 필요한 component 입니다.
Activity 는 android.app 에 있으며, ContextThemeWrapper 를 상속하고 여러 기능을 담당하기 위해 인터페이스들을 구현하고 있습니다. 그중에서 눈에 띄는 녀석은 Window 와 관련된 인터페이스들 입니다.
View #1 Android Ui 에도 한번 언급했듯이, Activity 는 Window 를 제공하고 있습니다. Window 는 앱의 도화지 역할을 하며, 개발자가 정의한 View 를 보여주기 위해 반드시 필요한 클래스입니다.
그밖에도 사용자와 앱간의 상호작용을 위한 여러 기능들을 제공해주고 있습니다. 안드로이드 공식 문서 에서 중요한 몇가지 기능들을 소개하고 있습니다. 그중에서 대표적인 몇가지를 보자면,
- Activity Lifecycle
- Task 와 Backstacks
- 상태 관리 솔루션
- 화면 또는 권한 요청과 결과 응답
- 프로세스 수명주기
주요 기능들을 먼저 살펴본뒤에, 내부코드를 보면서 이해를 해보는 순서로 가져가보겠습니다.
Activity Lifecycle
Activity 가 실행되는 진입점은 두가지로 분류됩니다.
첫번째는 project/app/manifest.xml 파일이 packageManager 에 의해 parsing 되고 난 뒤 앱런처 라는 앱에서 디바이스에 설치되어진 앱들이 노출되게 됩니다. 노출된 앱을 사용자의 상호작용에 의해 앱런처에서 앱이 실행(launch)되면서, manifest.xml 에서 intent-filter 로 action.MAIN 과 category.LAUNCHER 로 정의된 Activity 가 ActivityThread 의 main() 함수 내부에서 launch 되어집니다. 한가지 더 생각해보자면, 해당 Activity 를 외부(런처앱)에서 실행할 수 있어야 하기 때문에 manifest.xml 에서 Activity 태그에 exported = true 를 해둔다는 점입니다. 이것을 false 로 하면 앱 외부에서 실행할 수 없습니다.
두번째는 실행된 앱 프로세스 내에서 화면 요청 과정인 ContextWrapper#startActivity() 를 실행하여 Instrumentation#execStartActivity() 에 의해 activity thread 로 dispatch 되면 Activity 가 launch 됩니다.
두가지 방식 모두 ActivityThread 에 의해서 launch 과정이 수행되고 궁극적으로는 Instrumentation 에 의해서 인스턴스화 한 뒤 순서대로 정의된 콜백 메소드를 실행하게 됩니다.
콜백 메소드들을 하나씩 살펴보겠습니다.
Callbacks
onCreate()
Activity 생성 이후 가장 먼저 호출되는 콜백입니다. Activity 가 인스턴스화 된 후 부터 단 한번만 실행 되므로, 필요한 인스턴스들의 초기화와 같은 작업들을 이곳에서 수행해야 합니다.
파라미터로 전달되는 savedInstanceState bundle 객체는 상태 복원의 용도로 제공됩니다. 해당 값은 nullable 하며 복원할 상태가 없다면 null 이 전달됩니다.
중요한점은, 개발자가 정의한 뷰를 이곳에서 인스턴스화 한 뒤 Activity의 window 인스턴스에 존재하는 mContentparent 로 할당하는 뷰 연결 작업 을 수행해주어야 합니다. 이것을 수행하는 메소드는 xml 방식의 경우 Activity#setContentView() 혹은 compose 방식의 경우 ComponentActivity#setContent() 를 이용하시면 됩니다. 해당 내용은 View #1 Android Ui 포스팅에서 이미 다룬 내용이기도 합니다.
마지막으로 onCreate() 호출 이전에 Activity 가 인스턴스화 된 경우 Lifecycle State 는 INITALIZED 이고, onCreate() 가 호출된 후에 시스템은 ON_CREATE 이벤트를 호출하게 되고 Lifecycle State 는 CREATED 가 됩니다.
onStart()
onCreate() 가 호출된 직후 onStart() 가 실행됩니다. onStart()가 호출되면서 Activity 가 화면에 보여지기 시작합니다. 따라서 실행중인 앱의 프로세스 상태는 Visible 이 됩니다.
onStart() 호출 후 시스템은 ON_START 이벤트를 호출하게 되고 Lifecycle State 는 CREATED -> STARTED 가 됩니다.
onRestoreInstanceState()
onRestoreInstanceState(Bundle) 는 저장된 상태가 있는 경우 복원할 목적으로 실행되는 콜백입니다. 매개변수로 전달받는 bundle 객체는 non-null 하며, onSaveInstanceState() 가 실행되었더라도 상태를 복원할 필요가 없다면 onRestoreInstanceState() 는 실행되지 않습니다.
개발자에 의해 Activity 에서 사용하는 상태들이 복원되기도 하지만, 시스템에 의해 관리되는 backstack 이나 viewtree 정보들도 마찬가지로 내부 코드로 저장 및 복원됩니다. 따라서 개발자가 직접 코드 작업하지 않아도 view-tree 에 존재하는 scrollView 의 스크롤 위치나, EditTextView 에 있었던 text 값들 등이 android:id 태그 값이 할당되어 있다면 자동으로 저장 및 복원됩니다.
onResume()
onResume()이 호출되면, Activity 의 window 의 mContentParent 에 연결된 화면이 보여집니다. 화면이 사용자에게 보여진다는 것은 다른말로 focused(포커스를 받고 있음) 라고 얘기하며, Focused Activity 는 backstack 의 top 에 있으며, 프로세스 상태가 Foreground 가 됩니다. Activity Backstack 에 관련해서는 후술할 예정입니다.
onResume() 호출 후 시스템은 ON_RESUME 이벤트를 호출하게 되고, Lifecycle State 는 RESUMED 가 됩니다.
onPause()
onPause()는 onResume() 에 대응되며, Activity 의 window 가 일부 가려지게 됬을 때 호출됩니다. 즉, Dialog 와 같은 다른 window 가 열리면서 해당 Activity 의 window가 가려지며, 반투명으로 흐려져 보이게 됩니다. 그와 동시에 Activity 는 focus 를 잃게 되며, 프로세스 상태는 Visible 이 됩니다.
아직은 Activity 의 window 가 일부 보여지고 있으므로, UI 의 update 가 가능합니다. 또한, 해당 callback 은 빠르게 지나가기 때문에 DB 트랜잭션이나 네트워크 호출과 같은 비동기로 처리해야하는 작업들을 이곳에서 처리하면 안됩니다.
onPause() 콜백부터는 onCreate() ~ onResume() 과 대응되는 즉, 반전 형태를 가집니다. onPause() 호출 전 시스템은 ON_PAUSE 이벤트를 먼저 호출하고, Lifecycle State 는 STARTED 가 됩니다. 이후 onPause() 콜백을 실행합니다. onPause() 호출 이후 Activity 가 재개되는 경우 onResume() 콜백을 호출합니다.
onStop()
onStop() 은 onStart() 와 대응되며, Activity 의 window 가 더이상 보이고 있지 않다는 것을 의미합니다. 보통 시스템의 home 버튼이나 back 버튼으로 Activity 에서 이탈하는 경우 발생하며, configuration change 에서도 발생합니다.
아직 Activity 의 인스턴스가 메모리에는 남아있지만, windowManager 와의 연결은 제거됩니다. 보통 해당 callback 에서 실행중인 작업을 종료 및 리소스를 제거 해야 합니다. 예를들면, 더 이상 화면이 보여지지 않기 때문에 보통 ViewModel 에서 가지는 UI 관련 상태들의 수집 또는 비동기 작업을 이 콜백에서 종료합니다.
home 버튼으로 앱에서 이탈 시, App 프로세스 상태는 background 가 됩니다.(주의하세요. Activity 전환의 경우 프로세스 상태는 background 로 바뀌지 않습니다.) 이러한 경우 후술할 예정이지만 시스템이 메모리 부족으로 인해 언제든지 프로세스를 종료할 수 있으며, Activity 가 메모리에서 제거될 수 있습니다. 따라서, 사용자 경험을 보완하기 위해 다른 포스팅에서 서술한 바와 같이 상태 관리 #2 SavedState 메커니즘을 제공하고 있습니다. 또한 backstack 역시 마찬가지로 해당 매커니즘으로 관리되어 메모리 부족으로 인해 시스템이 프로세스를 종료시켰더라도 최근 화면에서 다시 앱을 실행하면 이전 진행 흐름(Activity backstack)을 유지할 수 있습니다. 아래 Lifecycle Scenario 에서 더 자세히 다루겠습니다.
onStop() 호출 전 시스템은 ON_START 이벤트를 먼저 호출하고, Lifecycle State 는 CREATED 가 되며, 이후 onStop() 이 실행됩니다. 또한, onStop() 이후 다시 Activity 가 재개되는 경우 onRestart() 콜백을 호출합니다.
onSaveInstanceState()
상태 복원은 시스템에 의해 프로세스가 메모리에서 제거되는 경우 사용자 경험을 보완하기 위해 이전까지의 실행 흐름을 저장 및 복원하여 재개 시점부터 이어나갈 수 있도록 도와주기 위해 제공되는 매커니즘 입니다. 그중 onSaveInstanceState() 는 상태를 저장하기 위한 콜백으로 반드시 호출되는 콜백은 아닙니다.
onSaveInstanceState() 는 사용자가 명시적으로 Activity 를 이탈하는 경우에 대해서는 호출되지 않습니다.(자세히는 onFinish() 를 호출한 경우) 앱이 정상적으로 종료된 경우 상태를 저장 및 복원할 필요가 없기 때문입니다. 즉, onSaveInstanceState() 는 다른 화면으로의 전환이나 시스템의 home 버튼에 의해 잠시 Activity 를 이탈한 경우, 마지막으로 configuration change 가 발생한 경우 onStop() 이후에 호출됩니다.
매개변수로 전달받는 bundle 객체에 상태를 저장해주면, 이에 대응되는 콜백인 onRestoreInstanceState(Bundle) 나 onCreate(Bundle) 콜백에 매개변수로 전달받는 bundle 객체로 복원하실 수 있습니다.
onDestroy()
onDestroy() 는 onCreate() 와 대응되며, Activity 가 더 이상 유효하지 않아 메모리에서 제거되기 직전에 호출되는 콜백입니다. onStop() 까지는 화면 전환이나 시스템 UI 의 home 버튼을 눌러 잠깐 Activity 를 이탈했을 때 호출되지만, 사용자가 앱의 backstack 의 마지막 Actvitiy 를 back 버튼으로 이탈하여 onFinish() 가 호출되거나 configuration change 로 인해 메모리에서 제거될 때 onDestroy() 가 호출될 수 있습니다. 따라서 메모리에서 제거되기 직전이므로 onStop() 에서 정리하지 않은 리소스를 반드시 여기서 제거해야 합니다. 그렇지 않으면 메모리 누수의 원인이 될 수 있습니다.
앱의 backstack 에서 마지막 Activity 가 제거되는 경우 앱 프로세스 상태는 EMPTY 가 됩니다. onDestroy() 호출 전 시스템은 ON_DESTROY 이벤트를 먼저 호출하고, Lifecycle State 는 DESTROYED 가 되며, 이후 onDestroy() 콜백이 실행됩니다.
이미 위에서 서술했듯이 lifecycle 관련 callback 실행은 전후에 시스템에서 특정 event 들을 실행합니다. 따라서 LifecycleOwner 구현체로부터 lifecycle 을 얻을 수 있다면, observer 패턴으로 callback 을 등록하여 lifecycle-aware 하게 특정 동작을 실행시킬 수 있습니다. 가장 흔한 예시로 androidx.lifecycle 의 repeatOnLifecycle() 확장 메소드는 Activity 또는 Fragment 에서 화면이 보이지 않는 경우 ViewModel 에서 수집하는 UI 관련 상태의 수집을 중지시키기 위한 용도로 만들어졌습니다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
uiStateFlow.collect { uiState ->
updateUi(uiState)
}
}
}
Lifecycle Scenario
해당 포스팅에서는 더 자세한 Activity 의 Lifecycle 을 알아보기 위해 몇가지 시나리오를 예시로 다뤄보려 합니다.
Single Activity
Activity 가 단 하나만 존재하는 경우에 대해 몇가지 시나리오를 더 자세히 살펴보겠습니다.
- 시스템 back 버튼을 누르거나 onFinish() 를 명시적으로 호출한 경우
Activity 가 onPause() ~ onDestroy() 까지 연속적으로 호출됩니다. 즉, Activity 가 메모리에서 제거됩니다. 하지만 onSaveInstanceState() 는 호출되지 않습니다. Backstack 에 하나의 Activity 만 존재했다면, 앱 프로세스 상태는 EMPTY 가 됩니다.
- Configuration Change 가 발생한 경우
Activity 가 onPause() - onStop() - onSaveInstanceState(bundle) - onDestroy() 까지 연속적으로 호출 된 후, 즉시 onCreate(bundle) - onStart() - onRestoreInstanceState(bundle) - onResume() 까지 호출됩니다. 위에서 설명한 바와 같이 이 때의 onCreate 의 bundle 파라미터는 null 이 아닙니다.
- Dialog 와 같이 다른 window 에 의해 일부가 가려진 경우
Activity 가 onPause() 까지 호출되면서 포커스를 잃고 반투명 으로 보여지게 됩니다. 다른 window 가 제거되는 경우 다시 onResume() 이 호출되며 재개됩니다.
- 다른 앱으로 전환되는 경우
Notification 을 클릭하거나 최근 화면에서 다른앱을 클릭하여 전환되는 경우 Activity 가 onPause() - onStop() - onSaveInstanceState(bundle) 까지 연속적으로 호출되고, 앱 프로세스가 background 로 전환됩니다. Background 상태인 앱 프로세스는 언제든지 메모리가 부족할 때 시스템에 의해 제거되어질 수 있기 때문에 상태 저장 및 복원을 위해 onSaveInstanceState 가 호출되어집니다.
이후 다시 원래 앱으로 돌아간다면 onRestart() - onStart() - onRestoreInstanceState(bundle) - onResume() 순서로 재개됩니다. onStop() 까지 호출되었다면 onStart() 가 아닌 onReStart() 부터 시작한다는 점이 다릅니다. 또한 만약, 앱 프로세스가 시스템에 의해 메모리에서 제거되었다면 onCreate(bundle) - onStart() - onRestoreInstanceState(bundle) - onResume() 순서로 재개됩니다. 여기서 알 수 있는 점은 시스템에 의해 제거된다면 onDestroy() 콜백은 실행되지 않는다는 것입니다. 만약, 특정 컴포넌트 또는 앱이 종료되었는지를 판단하기 위해 onDestroy() 콜백을 이용한다면 정상적으로 확인할 수 없을것입니다.
Mutli Activity
Activity 가 하나일 때와 두개일 때는 큰 차이는 없습니다. 위의 4가지 시나리오에서 두개의 Activity 간의 전환이 된 경우가 추가됬을 뿐입니다.
- Activity a 에서 b 로의 전환 후 시스템 back 버튼을 누르거나 onFinish() 를 명시적으로 호출한 경우
Activity a 의 onCreate - onStart - onResume 이 호출된 상태에서 b 로의 전환이 일어나면 a 는 onPause - onStop - onSaveInstanceState 까지 호출되며, 동시에 b는 onCreate - onStart - onResume 이 호출됩니다. 두개의 activity 의 lifecycle callback 호출은 병렬로 수행되어, 두 그룹간에 스위칭 될 수 있습니다. 다만, 한 액티비티내의 순서는 보장됩니다.(정확히는 a 액티비티의 onPause() 호출 후, b 액티비티의 onCreate - onStart - onResume 호출 후, a 액티비티의 onStop 이 호출됩니다.) 또한, a 액티비티 는 backstack 에 쌓이게 되고 메모리에서 제거된 것이 아닙니다. b 액티비티에서 back 버튼 혹은 onFinish() 가 호출된다면 b 액티비티는 명시적으로 종료되기 때문에 onPause - onStop - onDestroy 가 호출되고 제거되며, 동시에 a 는 onRestart - onStart - onResume 이 호출됩니다. a 의 경우 메모리에서 제거되지 않았기 때문에 상태는 저장했지만 복원할 필요가 없으므로 onRestoreInstanceState() 가 호출되지 않습니다.
- 만약 Activity a -> b 로 전환 후, 다른앱 으로 전환했다면?
위의 콜백 호출에서 볼 수 있듯이 b 로 전환할 때 시스템에 의해 종료될 경우를 대비하기 때문에 a 액티비티가 포커스를 잃으면서 onSaveInstanceState() 까지 호출됩니다. 마찬가지로 다른앱으로 전환 하면서 b 액티비티는 onPause - onStop - onSaveInstanceState 까지 호출됩니다.
- a -> b 로 전환한 상태에서 메모리 부족 등으로 인해 시스템에 의해 프로세스가 종료되고 다시 앱프로세스를 실행했다면?
상태를 저장했기 때문에 backstack 이나 view 계층구조 모두 복원되어 이전의 흐름에서 그대로 재개할 수 있습니다. 단지 두 Activity 모두 메모리에서 제거되었기 때문에 다시 앱 프로세스를 실행했을 때 Activity b 가 onCreate - onStart - onRestoreInstanceState - onResume 이 호출되며, b 에서 a 로 되돌아간다면 b 는 onPause - onStop - onDestroy 가 호출되며 Activity a 는 onCreate - onStart - onRestoreInstanceState - onResume 이 호출됩니다.
- a -> b 로 전환한 상태에서 configuration change 가 발생했다면?
Activity b 가 onPause - onStop - onSaveInstanceState - onDestroy 까지 호출된 후 즉시 onCreate - onStart - onRestoreInstanceState - onResume 이 호출됩니다. 즉 위에서 다룬 콜백이 그대로 실행됩니다. 이후 b 에서 a 로 되돌아간다면, Activity b 는 onPause - onStop - onDestroy 가 호출되며 Activity a 는 onCreate - onStart - onRestoreInstanceState - onResume 이 호출되게 됩니다.
뭔가 특이점이 느껴지셨나요? 맞습니다. 특정 상황에서의 lifecycle 변화가 focus 를 받고 있는 Activity 에서 일어난다는 점입니다. 이 규칙으로 이해하신다면, 별도로 해당 상황들에 대한 lifecycle 변화를 외울 필요없이 떠올리실 수 있을겁니다.
Task 와 backstack
Task 란 안드로이드에서 A task is a collection of activities that users interact with when trying to do something in your app. 라고 정의하고 있습니다. 즉, activity들을 유지하는 collection 입니다. 하지만 여러 activity 를 전환하면서 단순히 collection 으로 그 순서를 유지하기는 어렵습니다. 따라서 backstack 이라고 불리는 stack 구조의 LIFO 자료구조를 사용하여 activity 들의 순서를 유지합니다. 요약하자면, Task 에는 backstack 이라 불리는 스택 자료구조로 Activity 들을 가지고 있습니다.
Task 와 Process 의 개념은 다릅니다. Process 개념은 안드로이드 프레임워크가 Linux 기반이기 때문에 Linux 의 프로세스 개념과 같습니다. 하지만, Task 는 Activity 에 대한 메모리를 저장하고 있는 단위로 안드로이드에서 만들어진 개념입니다.
조금더 예시를 들어볼까요? 개발자가 만든 앱 A 의 Activity A1 에서 다른 앱 B 의 Activity B1 를 요청해서 어떤 결과값을 가져온다고 생각해 봅시다. B 앱에서 딥링크를 처리하고 호출가능하도록 exported = true 로 뒀다고 가정해봅시다. 그러면 프로세스 관점에서는 앱A 는 background 이고 앱B 는 Foreground 일것입니다. 하지만, Task 에서는 A1 과 B1 이 차례로 쌓여있을 것입니다.(여기서 B1 의 launchMode 를 standard 로 가정합니다.) 어떻게 다른 앱이 현재 Task 에 쌓일 수 있을까요? 실제로 Activity 는 Java Api Framework 의 SystemServer 로 분류되는 ActivityManagerService 에 의해 관리됩니다. 이를 이용한 Task 개념에서는 프로세스가 다르더라도 Activity 관점에서 바라보기 때문에 가능한 것 입니다.
런처앱 에서 앱을 실행했을 때 task 가 존재하지 않는다면, task 를 생성한 뒤 manifest 에 정의된 Launcher Activity 를 실행하게 됩니다. 하지만 개발자는 필요로 인해 task 가 있는 상태에서 다른 task 로 activity 를 실행하거나, 같은 task 안에서 같은 activity 를 재사용 하고 싶을 수 있습니다.(같은 task 안에서 default 로 같은 activity 를 두번 실행하면, backstack 에 호출 순서대로 두개가 쌓이게 됩니다.)
이를 위해 manifest 에서 activity 의 launchMode 태그로 실행 방법에 대해 명시할 수 있습니다.
- standard : default 모드 입니다. 호출 순서대로 backstack 에 무조건 쌓입니다. 즉, 같은 activity 가 여러번 호출되면 여러번 인스턴스화 하고 쌓입니다.
- singleTop : 만약, backStack 의 Top 에 호출하는 Activity 가 있다면, 재사용하고 onNewIntent() 를 호출합니다. 그렇지 않다면 standard 와 동일하게 실행됩니다.
- singleTask : taskAffinity(task 를 분류하는 단위) 가 같은 기존 task 에서 같은 activity 가 있는지 찾아 있으면, 새로운 태스크로 분리하여 재사용 하고 onNewIntent() 를 호출합니다. 이 때, task 의 backstack 에서 해당 activity 위에 존재하는 모든 activity는 소멸됩니다. 없다면, 새로운 task 에 root activity 로 만들어 실행합니다.
- singleInstance : Task 에는 하나의 activity 만 존재할 수 있습니다. activity 호출은 task 생성을 동반합니다.
- singleInstancePerTask : singleInstance 와는 달리 task 내에 다른 activity 는 실행될 수 있습니다.
보통의 앱에서는 standard 나 singleTop 으로도 충분할 것입니다. 필요에 따라 적절하게 사용하면 됩니다.
launchMode 태그 이외에도 Activity 실행시에 intent 로 flag 를 설정할 수도 있습니다.
- FLAG_ACTIVITY_NEW_TASK : launch mode 의 singleTask 와 동일하게 실행됩니다.
- FLAG_ACTIVITY_SINGLE_TOP : launch mode 의 singleTop 과 동일하게 실행됩니다.
- FLAG_ACTIVITY_CLEAR_TOP : 실행하는 Activity 가 이미 task 에 존재한다면, 재사용 하고 그위에 존재하는 모든 activity 를 소멸시킵니다. 따라서 실행하는 Activity 가 backstack 의 top 이 됩니다.
Intent 로 설정된 flag 는 launchMode 보다 높은 우선순위를 가집니다.
상태 관리
상태 관리 솔루션의 경우 이미 안드로이드 상태 관리 에서 다루었습니다. 해당 내용들은 1~3 로 3개의 챕터로 자세히 소개하고 있으니 생략하겠습니다.
화면 또는 권한 요청과 응답 처리
화면요청은 Api 30 미만에서는 contextWrapper#startActivity() 또는 Activity#startActivityForResult() 로 응답 처리가 가능합니다. 또한, 권한은 ActivityCompat#requestPermissions() 로 요청하고, Activity#onRequestPermission() 에서 응답을 처리할 수 있습니다.
API 30 이상에서는 ActivityResult*** 로 화면 또는 권한을 요청하고 callback 으로 응답을 처리합니다. 해당 자세한 내용들은 ComponentActivity 챕터에서 다루도록 하겠습니다.
프로세스 수명주기
프로세스 수명주기는 공식문서 에 자세히 설명되어 있습니다.
앱에서 사용자와 상호작용 하고 있는 Activity 가 존재하거나, BroadcastReceiver.onReceive() 가 실행중이거나, Service 의 수명주기 콜백이 실행중이라면 Foreground 상태로 유지됩니다. 그렇지 않은 경우 Visible 또는 background 에 위치할 수도 있습니다.
보통 앱의 비즈니스에 따라 background 에서 특정 작업을 수행할 필요가 있습니다. 음악 스트리밍 앱의 경우 다른앱 으로 전환하여 해당 앱이 보이지 않는 상태에서도 음악이 재생되어야 할 수도 있으며, 헬스 앱의 경우 앱이 보이지 않는 상태에서 걸음수를 수집해야 할 수도 있습니다.
안드로이드에서의 최근 업데이트 방향성은 background 작업에 대해 보안 및 사용자 경험 측면에서 개발자가 크게 사용하지 못하도록 제약을 추가하고 있습니다. 보통의 음악 스트리밍이나 헬스 데이터 앱은 ForegroundService 를 이용하여 Notification 을 통해 해당 앱이 보이지는 않지만 실행중임을 알려주도록 권고하고 있습니다. 그렇지 않은 경우 정해지지 않은 방식으로 background 에서 실행중인 앱은 동작하지 않거나 언제든지 시스템에 의해 종료될 수 있습니다. 또한 전원 관리에서 소개하는 앱 제한/대기/잠자기 모드 로 인해 background 작업에 대해 많은 제약이 존재합니다. 이에 대한 만병통치약은 ForegroundService 이지만 반드시 Notification 이 노출되어야 한다는 단점(?)도 존재하며 적절한 방법을 이용하여 안드로이드 환경에서의 요구를 충족시키는 앱을 개발하여야 합니다.
그외 자세한 설명은 공식문서를 보시길 권장드립니다.
댓글남기기