Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services.
일반 관측 가능과 달리 LiveData는 수명주기 인식 기능을 갖추고 있습니다.
따라서 Activity, Fragment 또는 services와 같은 다른 앱 구성요소의 수명주기를 고려합니다.
This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
수명주기 인식을 통해 LiveData는 활동상태에 있는 앱 구성 요소 관찰자만 업데이트할 수 있습니다.
구구절절하게 써놨지만, 정리하자면...
LiveData 는 Activity,Fragment, Services 과 같은 LifeCycle 가 고려되어 동작한다.
- STARTED, RESUMED : LiveData 활동상태 , Observer 객체 업데이트 가능 - 그 외 : LiveData 비활동상태, Observer 객체 업데이트 불가능
- DESTROYED : LiveData Observer 제거 가능
라고 볼 수 있을 것 같습니다.
2. LiveData 를 사용했을 때 장점은 무엇일까요?
1. UI 와 데이터 상태의 일치를 보장할 수 있습니다. ( Ensures your UI matches your data state )
LiveData 는 Observer Pattern을 따릅니다. 즉, LiveData 의 데이터가 변경될 때 마다 Observer 객체에게 알려줍니다. Observer 객체에서는 LiveData의 데이터가 변경될 때 마다 UI 를 Update 하는 로직을 작성해둔다면 UI 와 데이터 상태의 일치를 보장할 수 있어집니다.
2. 메모리 누수가 없습니다. ( No memory leaks )
메모리에 올라가있는 인스턴스를 앱이 더이상 필요하지 않음에도 GC 의 대상이 되지 못하면, 메모리누수가 발생합니다. LiveData 는 수명주기가 끝나면 자동으로 삭제됩니다. 즉, 메모리누수를 방지할 수 있습니다.
3. 중지된 액티비티로 인한 비정한 종료가 없습니다. ( No crashes due to stopped activities )
LiveData 를 관찰하고있는 Activity, Fragment, Services 등의 중지된 상태에서는 LiveData 의 변경사항에 대해 수신하지 않습니다. 따라서, LiveData 가 변경되었을 때 UI 를 수정하는 코드를 작성했다고 해도 동작하지 않으면서 비정상 종료에서 벗어날 수 있습니다.
4. 수동으로 수명 주기를 처리하지 않습니다.( No more manual lifecycle handling )
UI 를 구성하는 Component 들은 LiveData 를 관찰하기만 할뿐, 그 관찰을 중지하거나, 다시시작하는 행위를 하지 않습니다. 관찰하는 동안 LifeCycle 상태의 변경을 인식하기 때문에 이러한 사항들은 자동으로 관리합니다.
5. 항상 최신 데이터 유지합니다. ( Always up to date data )
만약 Oberver 객체가 백그라운드 상태가 되었을 때, 포그라운드 상태로 돌아오면, 그 즉시 LiveData 의 최신 데이터를 수신합니다. 따라서, 백그라운드 상태에 있을 때 데이터가 업데이트 되더라도, 포그라운드로 돌아오면 항상 최신 데이터를 유지합니다.
기기가 회전과 같은 구성(configuration) 의 변경으로 인한 Activity, Fragment 재생성등의 상황이 발생하면 사용가능한 LiveData 의 최신 데이터를 수신합니다. 따라서, 흔히 화면 회전시 사용해야 하는 Data 가 유실되는 경우를 쉽게 방지할 수 있어집니다.
7. 리소스를 공유합니다. ( Sharing resources )
LiveData 가 Observer 객체의 LifeCycle 을 인식할 수 있다는 것은, Activity, Fragment, Services 간에 객체를 공유할 수 있다는 것입니다. LiveData 를 상속받아서 새로운 LiveData 클래스를 만들고, 싱글톤 패턴을 사용하여 구현할 수 있습니다. <상세링크>
3. 오늘의 결론
LiveData 를 단순히 예제가 그러니까, 많은 코드들에서 쓰이고 있어서, 라는 이유로 사용하기 보다는
LiveData 의 장점들을 한번 더 기억하고, 그러한 장점을 살리는 코드를 작성해보는건 어떨까요?
Fragment 는 FragmentActivity 내에서 동작이나 UI(부분) 을 나타냅니다. ( FragmentActivity 는 추후 학습, Activity 의 한 종류라고 생각하고 넘어가시고 나중에 정리해두겠습니다. (Activity, AppBarActivity, AppcompatActivity, FragmentActivity 등등...)
2. Fragment 특징
1. 하나의 Activity 내에서 여러개의 Fragment를 결합하여 window 가 여러개인 UI 를 빌드할 수 있다.
2. Fragment 는 여러개의 Activity에서 재사용이 가능하다.
3. Fragment 는 자체적인 수명주기를 가지고, 자체적인 입력 이벤트를 수신할 수 있다.
4. Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다.
5. Fragment 는 항상 Activity 내에서 사용되어야 한다. 이 때, Fragment 수명주기는 Activity 의 수명주기에 직접적인 영향을 받는다.
6. Frgment 를 Activity 에서 사용하는 Layout 에 추가하는 경우 해당 Fragment 는 해당 Activity 의 ViewGruop 에 속하게 되고,
자체적인 View Layout 을 정의한다.
7. Fragment 는 Layout.xml 에서 <fragment> 요소로 선언하거나, 기존의 ViewGroup 에 추가하는 방법을 사용하면 Code 에서 Fragment 를 Layout 에 삽입이 가능하다.
3. Fragment LifeCycle
1. Fragment LifeCycle 가져오기 ( kotlin )
this.lifecycle.currentState
2. View LifeCycle 가져오기 ( kotlin )
this.viewLifecycleOwner.lifecycle.currentState
Fragme LifeCycle 단계 정리
Fragment LifeCycle
Method
View LifeCylce
explanation
INITIALIZED
onAttach
NULL
프래그먼트가 액티비티와 연결되는 순간에 호출
CREATED
onCreate
NULL
프래그먼트를 처음 생성할 때 호출
CREATED
onCreateView
INITIALIZED
프래그먼트에 UI 를 그려야하는 순간에 호출하고, 프래그먼트에 UI 를 그린다면 view 를 반환. 만약 프래그먼트가 UI 를 그리지 않는다면, null 을 반환
CREATED
onActivityCreated
INITIALIZED
프래그먼트생성이 완료되었다는 것을 액티비티에 알릴 때 호출
CREATED
onViewStateRestored
CREATED
프래그먼트에 저장된 상태를 복원했을 때 호출
STARTED
onStart
STARTED
프래그먼트가 화면에 보여지는 순간에 호출
RESUMED
onResume
RESUMED
프래그먼트가 사용자와 상호작용을 하는 순간에 호출
STARTED
onPause
STARTED
프래그먼트가 사용자와 상호작용을 하지 못하는 순간에 호출
CREATED
onStop
CREATED
프래그먼트가 화면에서 사라지는 순간에 호출
CREATED
onSavedInstanceState
CREATED
프래그먼트의 상태를 저장할 때 호출
CREATED
onDestroyView
DESTROYED
프래그먼트의 UI 가 사라져야 하는 순간에 호출
DESTROYED
onDestroy
NULL
프래그먼트가 제거될 때 호출
DESTROYED
onDetach
NULL
프래그먼트가 액티비티와 연결이 끊어질 때 호출
* LifeCycle 이 NULL 일때 State 를 가져오려고 하면 에러가 발생한다는 당연한 사실을 기억하세요
Activity LifeCycle 과 비교하면, 더 복잡한 LiceCycle 을 가지고 있지만, 완전히 전혀 다른 느낌의 구조는 아닙니다.
결국 우리는 Activity 내에서 Fragment 를 결합하여 사용하기 때문에 Fragment 는 사용된 Activity 의 LifeCycle 의 영향에서 벗어날 수가 없습니다.
그렇다면, Activity 에 Fragment 를 결합할 때 발생하는 전체적인 LifeCycle 은 어떻게 되는지 알아보겠습니다.
이 그림만 봐서는 와...뭐야 이거? 싶으실 수 도 있습니다.
Android Fragment 실습에서 해당 동작에 대해 직접적으로 Log 를 찍어보면서 확인하는 글을 작성하겠습니다. :)
4. Fragment 특징별 설명
1. 하나의 Activity 내에서 여러개의 Fragment를 결합하여 window 가 여러개인 UI 를 빌드할 수 있다.
- 스마트폰에서의 메모장을 생각해볼까요? 첫 화면에서 제목(TitleFragment)들을 보여주고, 해당 제목을 누르면 내용(MemoFragment) 을 보여준다고 해보겠습니다.
- 태블릿에서는 어떨까요? 좌측에 제목(TitleFragment)들을 보여주고, 우측에 내용(MemoFragment) 보여줄 수도 있습니다.
즉, 태블릿에서는 하나의 Activity 에 동시에 TitleFragment 와 MemoFragment . 즉, 여러개의 Fragment 가 결합되어 사용된다는 것을 알 수 있습니다.
2. Fragment 는 여러개의 Activity에서 재사용이 가능하다.
- 스마트폰에서 첫화면(MainActivity) 에서 TitleFragment 만 보여주었다고, 해당 제목을 클릭했을 시에 새로운 상세화면(DetailActivity) 에서 MemoFragment 만 보여줄 수 있습니다. 이렇게 여러 Activity 에서 필요로 하는 Fragment 만을 결합하여 사용할 수 있습니다.
3. Fragment 는 자체적인 수명주기를 가지고, 자체적인 입력 이벤트를 수신할 수 있다.
- 3. Fragment LifeCycle 설명 참조
4. Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다.
- 실습예정
5. Fragment 는 항상 Activity 내에서 사용되어야 한다. 이 때, Fragment 수명주기는 Activity 의 수명주기에 직접적인 영향을 받는다.
- 3. Fragment LifeCycle 설명 참조
6. Frgment 를 Activity 에서 사용하는 Layout 에 추가하는 경우 해당 Fragment 는 해당 Activity 의 ViewGruop 에 속하게 되고,
자체적인 View Layout 을 정의한다.
7. Fragment 는 Layout.xml 에서 <fragment> 요소로 선언하거나, 기존의 ViewGroup 에 추가하는 방법을 사용하면 Code 에서 Fragment 를 Layout 에 삽입이 가능하다.
- 실습예정
5. 오늘의 결론
1. 실습을 통해서 알아보겠지만, 가장 중요한것은 Fragment 의 LifeCycle 을 Activity LifeCycle 과 함께 신경써야 한다는 것입니다.
Fragment LifeCycle 만 생각한채로 진행하면, 그 과정도 결과도 쉽지 않을 수 있습니다.
2. 저는 1Activity n Fragment 를 지향하지 않습니다. 그렇다고 해서 n Activity 0 Fragment 를 지향하지도 않습니다.
때에 따라서 적절한 n 개의 Activity 와, 재사용성이 필요하고 고려되는 측면들에서 n 개의 Fragment 를 사용하는 등으로
적절하게 섞어주는게 좋을 것 같습니다.
만약, 하나의 DeviceSize 만을 지원하는 앱이면 n Activity 0 Fragment 로 해도 되나요? 라는 질문을 받는다면
저는 그 Device에서 지원하는 앱이 그 내부적으로 재사용 가능한 UI 가 없습니까? 를 묻고 싶습니다.
모든 Activity 로 Fragment 를 대체할 수 있고, 1Activity n Fragment 로도 n Activity 를 대체할 수 있습니다.
A가 좋으니 A만 쓴다.
B가 좋으니 B만 쓴다.
이런 생각보다는 A의 장점과 B의 장점을 고려하여 상황에 맞게 쓴다. 로 생각하시면 좋을 것 같습니다.
ps. 정말 극단적으로 A보다 B가 모든 측면에서 좋다면, A라는 기술을 사라질 것입니다. (그것이 라이브러리라면, 더이상 업데이트를 지원하지 않거나, 없애거나 하는 등의 결과가 나오겠죠?) ex. butterknife 는 공식적으로 업데이트하지 않겠다고 선언한 라이브러리입니다. viewBinding 을 사용하라고 안내하고 있습니다.
1. Android System 은 LifeCycle(생명주기) 의 특정단계에 해당하는 특정한 콜백 메소드를 호출하여 Activity Instance 의 코드를 시작합니다.
- Activity Class 에는 Main 이 없다.
- Activity Class 에는 LifeCycle 에 해당하는 콜백 메소드가 존재한다.
- Activity Instance 에서 콜백 메소드는 Android System 이 호출한다.
2. Activity는 앱이 UI를 그리는 window를 제공합니다. 이 window는 일반적으로 Screen을 채우지만 (Full-Screen) Screen보다 작고 다른 window 위에 떠 있을 수 있습니다. 일반적으로 하나의 Activity 는 앱에서 하나의 Screen을 구현합니다.
- Activity 는 UI가 있는 App Component(앱 구성요소) 이다.
- Activity 는 window을 가득 채울수도 있고, 그보다 작은 상태로 window위에 있을 수 있다.
- 일반적으로는 Activity 는 앱에서 하나의 Screen을 구현한다. * widnow 와 screen 은 다릅니다.
3. Activity LifeCycle
Activity 는 사진과 같은 LifeCycle 을 갖는다.
onCreate()
Android System 에서 Activity 를 Instance화하기 위하여 호출하는 메소드
* setContentView() 를 사용하지 않으면 어떻게 되나요? UI Layout이 없으니까 에러인가요?
onStart()
Screen 이 보여지는 순간에 실행됩니다.
어라? setContentView() 가 Screen 의 UI Layout 을 정의하는 거라면, 어짜피 보여질 때 onStart() 가 호출되니까 이때 setContentView() 를 사용해도 되지 않아요?
가능은 합니다. 그런데, 왜? setContentView() 를 onCreate() 에 쓰는걸까?
Docs 에 의하면,
onCreate() 활동이 처음 생성 될 때 호출됩니다.여기에서 뷰 생성, 목록에 데이터 바인딩 등 일반적인 정적 설정을 모두 수행해야합니다.이 메서드는 활동의 이전에 고정 된 상태 (있는 경우)를 포함하는 번들도 제공합니다. 라고 되어있습니다.
음? 으음? 싶으시다면, 한가지 예를 들어보겠습니다.
Bundle 에는 Activity Instance 의 상태들을 저장할 수 있습니다. ( 변수를 저장할수도 있습니다. ) 이때 Bundle 에 저장된 상태를 이용하여 layout 을 수정한다고 생각하면,
우리는 onStart() 에서 Bundle 에 접근하기 위한 대책을 추가해야합니다. (변수를 추가로 넣는 등)
또한 꼭 이 때문은 아니더라도, 문서에서 제공하는 onCreate() 의 정의에 맞게 사용하는게 옳바른 코드의 작성방법 이라고 할 수 있겠죠?
onResume()
이 메소드는 사용자와 상호작용을 하는 순간에 실행됩니다.
즉, 사용자의 터치이벤트, 키보드로 입력넣기, 핸드폰을 흔들 때 반응해야하기 등등은
이 메소드가 호출되어야 가능합니다.
Activity Running
만약 우리가 C,Java 등의 언어에서 main() 을 실행하고 종료되기 전까지는 Running 단계겠죠?
마찬가지로 onResume() 이 실행된 이후부터는 Running 단계입니다.
onPause()
이 메소드는 사용자와 상호작용을 하지 못하는 순간에 호출됩니다.
그렇다고, 화면이 사라지는 상태를 말하는 것은 아닙니다.
또한, 사용자와 상호작용을 하지 못할뿐, 화면에 사라진 상태는 아니기 때문에 UI 를 업데이트 할 수 있습니다.
* 위와같은 상황에서 다시 MainActivity 로 돌아간다면, MainActivity 가 사용자와 상호작용이 가능해지니까 onResume() 이 실행됩니다.
onStop()
이 메소드는 더이상 Activity 의 Screen 이, 사용자에게 보여지지 않는 순간에 실행됩니다.
하지만, Instance 가 없어진 상태는 아닙니다.
* 특정한 조건없이 1초마다 count를 증가하는 Thread 가 실행중이였다면, onStop() 이후에도 Thread는 멈추지 않고 count를 계속 증가시깁니다. 때에 따라서는 필요할수도, 굉장히 위험할수도 있습니다.
(불필요한 코드가 계속 돌고 있다는 것은 성능저하까지 이어질 수 있...)
onStop() 이후에 굳이 필요없는 코드는 잠시 멈춰두는 것을 추천합니다. 밑에 나오는 onRestart() 에서 다시 실행시키면 되죠!
onRestart()
onStop() 메소드가 실행된 이후에, Activity 의 Screen 이, 다시 사용자에게 보여지는 순간에 호출됩니다.
즉, onStop() 이후에 screen이 보여진다면 onStop() -> onRestart() -> onStart() 순으로 진행되고,
onCreate() 이후에 screen이 보여진다면 onCreate() -> onStart() 순으로 진행됩니다.
* onPause() 에서 onResume() 이 호출되고, onRestart()도 결국 onRestart() -> onStart() -> onResume() 이 호출되면서
onResume() 에 onStop() 이후에 얻어지는 데이터를 넣는 경우가 있습니다.
저는 onResume() 에서는 onResume() 상황에 맞는 코드.
onRestart() 에서는 onRestart() 상황에 맞는 코드 만을 작성하는 것을 추천합니다.
onDestroy()
이 메소드는 Activity 가 완전히 사라지는 순간에 호출됩니다. 문서에 의하면, 현재 Activity Instance 에서 finish 를 사용하는 경우에 호출되거나, Android System 에서 메모리절약을 위해 일시적으로 파괴하면서 호출이 된다고 하는데, 이는 isFinishing() 으로 구분이 가능하다고 작성되어 있습니다.
만약 finish 를 사용하거나, Back 키를 사용하는 등으로 Activity 가 사용자에 의해 파괴되는 경우에는 isFinishing() 은 true 를 반환합니다.
Android System 에서 메모리절약을 위해 파괴하는 거라면 false 를 호출합니다.
* onDestroy() 에서 isFinishing() 가 true 일 때 데이터를 저장하거나 하는 행위는 위험합니다.
( 쉽게 onDestroy() 에서 데이터를 저장하지 마세요. )
*Android System 은 Process 만을 kill 합니다.
*만약, Process 가 제거되는 순간을 경험하고 싶다면, 개발자옵션 -> 프로세스수제한 -> 개수선택
이후 다른 앱들을 실행시켜보세요. 개수가 가득차면 Process 가 제거되면서 false 를 리턴합니다.
4. 정리
1. Activity 는 Android System 가 콜백메소드(onCreate)를 호출하여 생성하며 콜백메소드(Create~Destroy)는 LifeCycle 에 맞게 호출된다.
2. Activity 는 UI Layout을 정의하여 screen에 보여줄 수 있고, 만약 UI Layout 을 정의하지 않는다면 빈 screen이 보여진다. 또한 UI 가 없기 때문에 사용자에 맞춤 기능이 아니라, LifeCycle 에 해당하는 작성된 코드들만이 실행된다.
onCreate() : Android System 에서 Actvitiy 를 Instance화 하는 순간
onStart() : Screen 이 보여지는 순간
onRestart() : onStop() 이후에 Screen 이 보여지려고 하기(onStart()) 직전
onResume() : 사용자와 상호작용이 일어나기 시작하는 순간
onPause() : 사용자와 상호작용이 끊어지는 순간
onStop() : Screen 이 보여지지 않는 순간 : 단 1pixcel 이라도 Screen 이 보여진다면, onStop() 에 진입하지 않는다.
onDestroy() : Code(finish) or 사용자에 의해(back or finish) or AndroidSystem 이 Process 를 kill 하는 순간 - isFinishing()으로 구분
5. 오늘의 결론
생명주기에 맞게 코드를 작성하고, Android 문서는 영어로 보거나, 영어와 한글을 같이 보자. ( Window 랑 Screen 을 둘다 "화면" 이라고 번역시켜놔서 뇌에 혼란이 엄청왔다. )
PS.
일반적으로 1Activity 는 1개의 Screen 을 구현한다. 라고 되어있는 부분에서
1Activity 에서 어떤 경우에 2개 이상의 Screen 을 구현하는지는 좀더 찾아봐야 할 것 같습니다.
여러 의견 모음 (2021.04.15 기준)
1. Fragment 를 이용하여 화면에 보여주는 View 가 다른데, 이런 경우에 2개 이상의 Screen 을 구현하는 것 같다. - 저는 Activity 가 Window 에 Screen 을 보여주고, 이때 Screen 은 setContentView() 를 사용하여 정의한다고 되어 있는 부분에서 이 부분이 의아했습니다. Fragment 에 대해서는 추가 글에서 정리하겠지만, Fragment 는 Activity 이후에 나온 기술로, Activity 에 대해 설명하는 기준에 있어서 Fragment 를 사용하는 경우를 2개 이상의 Screen 을 구현했다고 생각하지 않습니다.
뭔가 근본적으로 Screen 에 대한 정의가 확실히 잡혀있지 않은 상태여서 그 부분을 찾아보고 있지만, Fragment 가 나오기 전부터 Screen 을 2개 이상 구현할 수 있었고, 그 로직의 단점을 보완하고자 Fragment 가 등장한것이 아닐까 라는 생각을 합니다.
이 부분에 대해 정확하게 설명이 가능한 블로그 or 지식을 보유중이신 분이 있으시다면 공유부탁드리겠습니다..