운기의 블로그
#2. 안드로이드 개발자 가이드 핵심주제, 액티비티 본문
1. 포스팅 읽는 방법
본 내용은 다른 개발은 해봤지만, 안드로이드를 처음 배우는 사람들이 쉽게 이해할 수 있도록 안드로이드 개발자 가이드를 바탕으로 설명할 것입니다.
문서의 내용을 전부 다루지는 않을 것 입니다.
큰 흐름 속에서 중요한 항목을 위주로 설명 할 것이기 때문에, 개발자 가이드 문서를 켜 두시고, 포스팅 된 내용을 읽다가 궁금한 부분이 있으시면 검색해 보시면서 보는 것을 추천 드립니다.
2. 목차
핵심 주제
- 액티비티
- 액티비티 소개
- 액티비티 생명 주기
- 작업 및 백 스택
- 프로세스 및 앱 생명 주기
위의 목차들은 시스템이 액티비티를 어떻게 관리하는지에 대한 내용으로 이루어져 있습니다.
" 액티비티는 사용자와 상호작용을 할 수 있는 화면을 제공하는 컴포넌트 "
액티비티의 생명주기에 대해 설명하기 전, 앱 기초에서 가장 중요했던 액티비티에 대해서 살펴보겠습니다.
안드로이드 앱을 구성하는데 액티비티는 필수적인 컴포넌트로 사용자와 상호작용을 할 수 있는 화면을 제공합니다.
이런 액티비티는 앱에 하나 이상의 존재하고, 앱 안에 존재하는 여러 개의 액티비티들은 하나의 앱 안에서 액티비티들을 활성화 하기도 하고, 다른 앱의 액티비티를 활성화 시키기도 합니다.
그럼 이런 액티비티들은 시스템이 어떻게 생성하고 관리하는지 알아보도록 하겠습니다.
3. 본문
액티비티의 가장 큰 특징이자 장점은 컴포넌트로 구현되어있다는 점입니다. 컴포넌트로 구현되어있는 액티비티들은 각각 앱을 시작 할 수 있는 지점이 됩니다.
" 액티비티는 컴포넌트 "
예를 들면 안드로이드 애플리케이션이 아닌 윈도우 애플리케이션의 경우 시작하기 위해서 아이콘을 클릭하게 됩니다. 그 이후 다른 윈도우 애플리케이션을 사용하기 위해서는 다른 아이콘을 클릭해서 실행해야합니다.
좀 더 자세히 설명하자면 메모장을 사용하는 중에 메모장을 통해 그림판을 실행시키는건 불가능 합니다. 그림판을 실행시키기 위해서는 그림판 아이콘을 클릭해서 실행해야 한다는 말입니다.
하지만 안드로이드 애플리케이션에서는 카카오톡을 실행하다가 카카오톡에서 카메라 애플리케이션을 실행 할 수 있습니다. 이러한 동작이 가능한건 액티비티가 컴포넌트로 이루어져 있기 때문입니다.
이렇게 액티비티에서 다른 액티비티로 전환할때 사용자들에게 자연스러운 화면 전환을 제공해야 합니다.
이런 화면 전환의 경우는 2가지 입니다.
1) 현재 액티비티에서 다른 액티비티를 새로 시작하는 경우
2) 현재 액티비티에서 뒤로가기 키를 이용해 이전의 액티비티를 시작하는 경우
첫번째 현재 액티비티에서 다른 액티비티를 새로 시작하는 경우에 대해서 먼저 알아보도록 하겠습니다.
현재 사용자가 사용중인 액티비티는 앱 내의 다른 액티비티를 활성화 시킬 수도 있고, 다른 앱의 액티비티를 활성화 시킬 수도 있다. ( 이건 앱 기초에서 다룬 인텐트에 의해 가능하다는 것도 여러분들은 알고 있을 것이다. )
이런 전환은 사용자들에게는 너무나도 자연스러운 현상이지만 실제로는 생각보다 복잡한 문제가 발생한다.
현재 사용자가 사용하는 액티비티 대신 새롭게 활성화 된 액티비티가 사용자에게 보이게 됩니다. 그러면 기존에 사용하던 액티비티의 내용은 어떻게 되는가?
이런 문제를 해결하기 위해서 액티비티의 생명주기가 존재합니다.
지금부터는 액티비티의 생명주기에 대해서 알아보고 사용자의 관점에서 생명주기가 왜 필요한지, 시스템의 입장에서의 생명주기가 왜 필요한지 에 대해서 설명하겠습니다.
" 생명주기는 액티비티가 생성되면서 파괴 될 때 까지의 흐름 "
액티비티의 생명주기는 액티비티가 생성되면서 파괴될때까지의 흐름으로 시스템이 액티비티의 상태에 따라 적절하게 콜백 함수를 실행하는 것을 말합니다.
생명주기는 onCreate , onStart, onResume, onPause, onStop, onDestory로 나누어 지고, 각각의 함수들은 액티비티의 상태를 보여줍니다. 6개의 생명주기는 크게 액티비티가 실행되는 흐름 ( = 전체 생명주기 ) , 액티비티가 사용자의 눈에 보이는 흐름 ( = 가시적 생명주기 ) , 액티비티가 화면의 최상단에 보이는 흐름 ( = 포그라운드 생명주기 )로 나눠집니다.
자세한 내용은 아래 생명주기 그림과 함께 설명하겠습니다.
1) onCreate( )
이 콜백 함수가 실행되면 액티비티는 생성이 됩니다. 그렇기 때문에 액티비티 생성전에 초기화에 관련된 내용을 onCreate 함수 안에 작성해야 합니다. setContentView( ) 함수를 통해 액티비티에 사용될 레이아웃과 findViewById( ) 함수를 통해 뷰에 관한 데이터를 묶어 주는 동작을 해야 합니다.
( 콜백 함수는 특정 시기에 호출되는 함수를 말합니다. )
2) onStart( )
create 콜백 함수가 끝나면 실행되는 함수 입니다. 이 콜백 함수가 실행되고 나면 액티비티는 사용자가 볼 수 있는 상태로 변환됩니다. 상당히 빠른 시간내에 완료되기 때문에 onStart에서 시간이 오래걸리는 작업을 진행 하면 안됩니다.
-- create나 start와 resume 사이에서는 기존에 작업했던 액티비티의 내용을 그대로 가져올 수 있습니다. 그 이유는 새로운 액티비티를 생성할 때 기존의 액티비티가 가지고 있던 정보를 Bundle 이라는 객체에 저장해주기 때문입니다.
Bundle을 통해 데이터를 가져오는 방법은 아래와 같습니다.
3) onResume( )
onStart가 끝나고 나면 실행되는 콜백 함수로, onResume 함수가 호출 되고 나면 비로소 최상단으로 보여지고
사용자와 상호작용을 할 수 있는 상태가 됩니다. 사용자가 다른 액티비티를 실행시키기 전까지 계속해서 유지됩니다.
-- 그림 2와 그림 3 처럼 저장된 데이터를 불러오기 위해서는 Bundle 객체에 데이터를 담아줘야 합니다.
이 때에는 레이아웃에서 id 값을 가지고 있는 View 객체에 관련된 UI 상태를 주로 저장합니다.
Bundle에 데이터를 담는 방법은 아래와 같습니다.
( 데이터베이스나 사용자의 기본 설정, 네트워크와 같이 자원을 많이 소모하는 작업은 주로 onStop에서 저장 )
4) onPause( )
onPause가 호출 될 때는 , 다른 액티비티를 실행시키고 현재 사용하는 액티비티는 화면의 최상단에서 밀려나게 됩니다.즉, 사용자의 관심이 현재 액티비티보다는 다른 액티비티를 보고 싶어 할 때 입니다. onPasue 함수는 onStart 함수와 마찬가지로 빠른 시간내에 지나가기 때문에 간단한 액티비티의 데이터는 저장 가능하지만, 과도한 작업을 주면 안됩니다. onPause 상태에서 기존의 액티비티로 돌아가게 되면 onResume 함수가 호출되게 됩니다.
( 과도한 작업 = 네트워크 호출, 데이터베이스 트랜잭션 .. )
5) onStop( )
사용자가 사용하던 액티비티는 다른 액티비티에 의해서 보이지 않게 되면 onStop 함수는 호출됩니다.
이 때, 보이지 않게 되는 액티비티가 가진 리소스를 제거하거나 조정해야 합니다. 대체로 안드로이드 운영체제의 자원을 많이 사용하는 작업들을 제거하는데, 네트워크나, 데이터베이스에 관련된 작업들 입니다. 다시 이전에 액티비티를 실행하고 싶으면 onRestart( ) 함수를 호출하여 액티비티를 onStart 부터 시작 할 수 있게 합니다.
6) onDestory( )
이 콜백 함수가 실행되게되면 액티비티는 시스템에서 없어집니다. 액티비티가 완전히 없이지는 경우는 사용자가 액티비티를 완전히 종료하거나 finish( ) 함수가 호출되거나, 안드로이드 운영체제를 사용하는 기기의 화면 회전의 경우 시스템이 일시적으로 액티비티를 없애는 경우가 있습니다. 그리고 onStop에서 제거하지 않은 모든 리소스를 제거합니다.
이런 생명주기는 왜 필요할까요?
" 사용자의 관점에서는 액티비티의 생명주기 ( Life Cycle )는 자연스러운 UI를 제공하기 위해 존재"
각각의 생명주기들은 액티비티의 상태를 나타내고, 서로 다른 작업들을 처리합니다.
이렇게 생명주기에 맞춰 코드를 작성해 적시에 데이터를 저장하고 복원해주면 사용자들이 앱을 사용하는데 불편함을 느끼지 않게 됩니다.
지금까지 생명주기가 무엇인지 확인하고, 새로운 액티비티가 활성화 될 때 기존의 데이터를 언제, 어떻게 저장하는지 그리고 언제 액티비티를 복원해주는지 알아봤습니다.
그러면 두번째 뒤로가기를 통한 액티비티의 전환에 대해서 알아보도록 하겠습니다.
우리는 기존에 액티비티 안에 데이터를 Bundle에 담아서 저장하고 있다는 거를 생명주기를 통해 알아봤습니다.
그렇다면 액티비티는 자체는 어디에 저장이 되어있을까요??
" 뒤로가기가 가능한 이유는 백스택 때문에 가능 "
백 스택은 앱에서 실행되는 액티비티들을 담아 두는 역할을 합니다.
그리고 이렇게 하나의 앱에서 실행되는 액티비티들을 저장하고 있는 스택 하나를 TASK( = 작업 )이라고 합니다.
액티비티가 스택에 쌓이는 과정은 아래와 같습니다.
intent를 통해 액티비티를 활성화 시킵니다.
활성화되는 액티비티는 생명주기에 따라 onCreate 메소드가 실행됩니다.
실행된 액티비티는 스택에 쌓이게 됩니다.
이후 사용자가 Back 키를 누르면 스택에 현재 활성화되는 액티비티는 pop이 되면서 사라지게되고, 그 다음에 있던 액티비티가 최상단으로 올라오게 되면서 사용자에게 보여지게 됩니다.
이렇게 앱의 액티비티를 관리하는 스택에 대해 알아봤습니다.
여기서 알아야 할 것은 안드로이드 운영체제를 사용하는 기기에는 여러개의 앱이 존재한다는 것입니다.
그리고 사용자는 실제로 여러개의 앱을 실행시킵니다.
이런 경우 시스템에는 여러개의 작업이 생기게 되고, 시스템이 가지고 있는 메모리의 크기 만큼 작업이 생성되면,
꼭 실행되어야 하는 다른 앱을 실행시킬 수 없게 됩니다. 그뿐만 아니라 정말 중요한 앱을 실행하는 도중 전화가 왔을 때, 중요한 앱을 종료시킬 수도 있습니다.
이런 경우를 방지하기 위해서 안드로이드 운영체제는 어떻게 앱을 관리할까요??
" 안드로이드 운영체제가 앱을 관리하는 방법 "
안드로이드 앱은 프로세스 위에서 실행됩니다.
좀 더 자세히 말하면 안드로이드를 구성하는 컴포넌트 중 사용되는 컴포넌트가 프로세스의 메인스레드를 차지해서 앱이 실행되는 것입니다.
그리고 이런 프로세스는 시스템이 관리합니다.
그렇기 때문에 앱을 종료 했다고해서 프로세스가 종료되는 것은 아닙니다.
시스템에서 프로세스를 종료해야지 비로소 프로세스가 종료되고, 메모리 공간을 반납하게 됩니다.
( 절대 시스템이 액티비티를 직접 종료 x, 메모리에는 프로세스가 계속 존재 )
그렇다면 계속해서 프로세스가 존재한다면 시스템은 어떤 프로세스를 종료하는 것일까요??
종료 할 프로세스는 우선순위에 의해 결정됩니다.
프로세스의 우선순위는 중요도 순으로 포그라운드 프로세스, 가시적 프로세스, 서비스 프로세스, 캐시된 프로세스, 빈 프로세스 이렇게 5가지로 나워지게 됩니다. 시스템은 메모리가 부족한 경우나 오랫동안 사용되지 않아 메모리를 낭비하는 프로세스를 중요도가 낮은 순으로 종료하게 됩니다.
그러면 프로세스의 우선순위는 어떻게 알 수 있을까요?
" 안드로이드 운영체제의 관점에서는 액티비티의 생명주기 ( Life Cycle )는 메모리를 관리하기 위해 존재 "
프로세스의 우선순위는 액티비티의 생명주기와 연관이 있습니다.
1. 포그라운드 프로세스
사용자가 액티비티를 실행시키고, 상호작용을 하는 onCreate, onStart, onResume 단계입니다.
실제로 사용자의 포커스가 넘어오거나, 포커스를 가지고 있는 상태로 사용되고 있기 때문에 가장 중요합니다.
그렇기 때문에 시스템에서 프로세스를 종료시키지 않습니다.
2. 가시적 프로세스
사용자의 포커스가 떠나가면서 액티비티의 상태가 pause가 된 상태입니다.
3. 서비스 프로세스
액티비티가 종료되었지만, 컴포넌트 중 서비스가 실행되고 있는 상태입니다.
현재 서비스를 통해 앱이 실행되고 있는 상태이기 때문에 onStop, onDestroy 된 액티비티보다 우선순위가 높습니다.
4. 캐시된 프로세스
완전히 사용자의 포커스가 떠난 onStop 상태로, 사용되지않고 백그라운드로 밀린 상태입니다.
5. 빈 프로세스
마지막으로 앱을 종료해 액티비티가 파괴된 onDestroy 상태입니다.
이처럼 생명주기는 안드로이드의 운영체제 한정되어있는 자원을 보다 효율적으로 사용하기 위해서 존재한다.
4. 마무리
이번 포스팅에서는 액티비티에 관련된 내용을 다뤘습니다.
액티비티는 컴포넌트로 구성되어있기 때문에 액티비티간의 전환이 가능합니다.
전환 방법은 현재 액티비티에서 다른 액티비티 전환 / 현재 액티비티에서 이전 액티비티 전환 2가지 방법이고
첫번째 방법에서 발생하는 데이터 저장 문제를 바탕으로 사용자 관점에서 생명주기가 왜 필요한지 설명을 진행했습니다. 두번째 방법을 설명하면서 백스택과 테스크에 대한 내용을 설명, 이 과정에서 발생하는 메모리 문제를 바탕으로 시스템은 메모리를 위해 어떻게 앱을 관리하는지에 대해 설명, 마지막으로 안드로이드 시스템 관점에서 메모리를 관리하기 위해 생명주기가 왜 필요한지에 대해 설명한 글 입니다.
이번 글은 뭔가 흐름이 자연스럽지 않아서 마무리에서 어떤 과정으로 생각하고 글을 썻는지 정리했습니다.
제가 생각하는 가장 큰 흐름에서만 작성한 포스팅이고, 필요한 내용을 꼭 찾아 보시길 바랍니다.
'안드로이드' 카테고리의 다른 글
ViewPager2 터치이벤트 분리하기 ( 터치 / 스와이프 ) / 뷰페이저2 클릭시 툴바 사라지게 하기 (0) | 2021.01.31 |
---|---|
#3. 안드로이드 개발자 가이드 핵심주제, 사용자 인터페이스 (0) | 2020.08.02 |
#1. 안드로이드 개발자 가이드 앱 기초 (0) | 2020.06.17 |
자바로 keyHash 구하는 소스 (0) | 2020.05.14 |
#6. 나만의 메모장 만들기 메모 클릭 시 작성된 메모 보기 / 메모 수정 / 메모 ROOM UPDATE / JAVA로 구현 (0) | 2020.04.07 |