1. Fragment 란?

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

https://developer.android.com/guide/fragments/lifecycle

 

 

1. Fragment LifeCycle 가져오기 ( kotlin )

this.lifecycle.currentState

onCreateView 에서 해당 코드 출력결과

2. View LifeCycle 가져오기 ( kotlin )

this.viewLifecycleOwner.lifecycle.currentState

onCreateView 에서 해당 코드 출력결과

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 은 어떻게 되는지 알아보겠습니다.

 

https://stackoverflow.com/posts/58029686/revisions

이 그림만 봐서는 와...뭐야 이거? 싶으실 수 도 있습니다.

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 을 사용하라고 안내하고 있습니다.

 

 

작성하다보니까 깨달은 것은 실습 글을 빨리 작성해야겠네요.

 

developer.android.com/reference/android/app/Fragment

 

Fragment  |  Android 개발자  |  Android Developers

 

developer.android.com

 

'BackUp (관리중지) > Android 이론' 카테고리의 다른 글

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
[Android Jetpack] LiveData란,  (0) 2021.04.21
Activity 와 Activity LifeCycle 분석  (0) 2021.04.15
Android Context 에 대한 분석  (1) 2021.04.14

1. AndroidStudio New Project

배포 전 테스트를 위해서 새프로젝트를 생성합니다. 

 

 

2. New Module ( Type : Library )

모듈을 추가해 줍니다.

 

3.Edit Func

클래스나, 기본파일 등 만들고자 하는 기능들에 대한 코드를 작성하여 줍니다.

 

4. Project Add Lib ( Project Structure - Dependencies - app - Add Module - Check - Apply - OK)

배포하기전에 프로젝트에 모듈을 등록시켜줍니다. (잘 동작하는지는 테스트 해야하니까요! - Must 는 아닙니다.)

 

5. Test

원하는 테스트로 정상적인지 확인해줍니다.

 

6. GitHub Push

GitHub 에 프로젝트를 Push 해줍니다.

 

7. GitHub Releases ( Releases - Create a new release - Write - Publish release )

Releases 로 버전관리를 해주어야 합니다. (등록등록)

 

 

8. https://jitpack.io/

 

JitPack | Publish JVM and Android libraries

JitPack makes it easy to release your Java or Android library. Publish straight from GitHub or Bitbucket.

jitpack.io

Jitpack 에 접속하여
[GitHubUserName]/[ProjectName]  을 작성하고 Look up 을 눌러줍니다.

 

8.1 [GitHubUserName]/[ProjectName]  & Look up

Fail : 1.0.0 -> Red Log Image & Report

Success : 1.0.1 -> Green Log Image & Get it

 

 

8.2 How to

자, 이제 끝났으니 제대로 동작하는지 테스트 해보겠습니다.

 

 

9. New Project

새프로젝트를 만들어줍니다. (실제 사용될 프로젝트가 되겠지요)

 

 

10. build.gradle

build.gradle 을 수정해줍니다.

이 곳에 작성하는 내용은 Jitpack.io 에서 How to 를 통해 알 수 있었습니다.

 

11. USE

사용을 하면 됩니다~!

12. Lib Update

라이브러리를 업데이트할 때는 코드가 수정되어 Push Push Babe 를 해준 후에

Releases 에서 버전을 올려서 update 하는 식으로 진행하시면 version 에 맞게 사용이 가능합니다.! (버.전.관.리.필.수.)

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

 

https://developer.android.com/reference/android/app/Activity

 

Activity 는 사진과 같은 LifeCycle 을 갖는다. 

 

onCreate()

Android System 에서 Activity 를 Instance화하기 위하여 호출하는 메소드

 

이 메소드에서는 setContentView() 를 이용하여 UI Layout 을 정의할 수 있습니다. 

* setContentView() 를 사용하지 않으면 어떻게 되나요? UI Layout이 없으니까 에러인가요?

 

setContentView() 를 작성하지 않으면, UI Layout 이 정의되지 않은 window 위에 빈 screen이 생성됩니다.
Activity 는 Screen보다 작은 크기로 다른 window 위에 존재할 수 있다 역시 성립합니다.

onStart()

Screen 이 보여지는 순간에 실행됩니다.

 

어라? setContentView() 가 Screen 의 UI Layout 을 정의하는 거라면, 어짜피 보여질 때 onStart() 가 호출되니까 이때 setContentView() 를 사용해도 되지 않아요?

 

setContentView() 를 onStart() 에서 호출한 경우

가능은 합니다. 그런데, 왜? setContentView() 를 onCreate() 에 쓰는걸까?

Docs 에 의하면,

onCreate()
활동이 처음 생성 될 때 호출됩니다.
 여기에서 뷰 생성, 목록에 데이터 바인딩 등 일반적인 정적 설정을 모두 수행해야합니다.이 메서드는 활동의 이전에 고정 된 상태 (있는 경우)를 포함하는 번들도 제공합니다.
라고 되어있습니다.

음? 으음? 싶으시다면, 한가지 예를 들어보겠습니다.

Bundle 에는 Activity Instance 의 상태들을 저장할 수 있습니다. ( 변수를 저장할수도 있습니다. ) 이때 Bundle 에 저장된 상태를 이용하여 layout 을 수정한다고 생각하면,

우리는 onStart() 에서 Bundle 에 접근하기 위한 대책을 추가해야합니다. (변수를 추가로 넣는 등)

savedInstanceState 가 null 이므로, USER 에 해당하는 activity_next 를 layout UI 로 정의

또한 꼭 이 때문은 아니더라도, 문서에서 제공하는 onCreate() 의 정의에 맞게 사용하는게 옳바른 코드의 작성방법 이라고 할 수 있겠죠?

 

onResume()

이 메소드는 사용자와 상호작용을 하는 순간에 실행됩니다.

즉, 사용자의 터치이벤트, 키보드로 입력넣기, 핸드폰을 흔들 때 반응해야하기 등등은

이 메소드가 호출되어야 가능합니다.

 

Activity Running

만약 우리가 C,Java 등의 언어에서 main() 을 실행하고 종료되기 전까지는 Running 단계겠죠?

마찬가지로 onResume() 이 실행된 이후부터는 Running 단계입니다.

 

onPause()

이 메소드는 사용자와 상호작용을 하지 못하는 순간에 호출됩니다.

그렇다고, 화면이 사라지는 상태를 말하는 것은 아닙니다.

또한, 사용자와 상호작용을 하지 못할뿐, 화면에 사라진 상태는 아니기 때문에 UI 를 업데이트 할 수 있습니다.

MainActivity 의 상호작용은 멈추었지만, 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 에 해당하는 작성된 코드들만이 실행된다.

 

3. 일반적으로 1개의 Activity 는 1개의 Screen 을 구현한다.

 

4. Activity 의 window 와 screen 은 다르다.

 

5. LifeCycle 은 onCreate() , onStart(), onRestart(), onResume(), onPause(), onStop(), onDestroy()로 이루어져있다.

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 지식을 보유중이신 분이 있으시다면 공유부탁드리겠습니다..

'BackUp (관리중지) > Android 이론' 카테고리의 다른 글

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
[Android Jetpack] LiveData란,  (0) 2021.04.21
Fragment 와 Fragment LifeCycle 분석  (0) 2021.04.19
Android Context 에 대한 분석  (1) 2021.04.14

Android 앱을 "한 번" 이라도 해본 사람은 Context 를 보지 못했을 수가 없습니다.

저는 Context 를 사용할 때, Context 가 대체 뭐야? 하고 상위클래스를 따라가보다가 수천줄에 기겁하고, 닫아버렸던 기억이 있네요.

그렇게 얕게 Context 가 무엇인지 대충 블로그를 통해 학습하고, 넘겼었습니다.

 

이번엔 그런식으로 넘기는게 아니라, 다시 한 번, Context 에 대하여 분석을 해볼까 합니다.

 

Android Reference 에 나온 설명
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

애플리케이션 환경에 대한 글로벌 정보에 대한 인터페이스입니다. Android 시스템에서 구현을 제공하는 추상 클래스입니다. 이를 통해 애플리케이션 별 리소스 및 클래스에 액세스 할 수있을뿐만 아니라 활동 시작, 인 텐트 브로드 캐스트 및 수신 등과 같은 애플리케이션 수준 작업에 대한 상향 호출도 가능합니다.

응 블로그 열면 거의 인삿말이이였지, 그래서?


1. Context 는 글로벌 정보에 대한 인터페이스입니다. (Context 는 추상클래스입니다.)

2. Context 는 Android 시스템에서 구현을 제공합니다. 

 

일단 이 2가지에 대한 설명을 기억한채로 이어가겠습니다.

 

 

 

 

Context를 직접적으로 상속받는 하위 클래스와 간접적으로 상속받는 하위 클래스 (간접으로 상속받는 하위 클래스는 더 많습니다.)

 

일단, 우리에게 익숙한 클래스인 Application 과 Activity, 그리고 Service 를 보였습니다.

더보기

Android Component

  • Activity  : Context 의 하위 클래스
  • Service : Context 의 하위 클래스
  • Broadcast receiver : public abstract class BroadcastReceiver { ... }
  • Content provider : public abstract class  ContentProvider implements ContentInterface, ComponentCallbacks2 {...}

 

위에 작성한 것처럼, Android Component 중에서는 Activity 와 Service 만 Context 의 하위 클래스입니다.

 

아, 그렇다면 Context 가 최상위에 존재하고, Application , Activity, Service 들은 하위 클래스 이므로, 서로 다른 Context 가 생성되겠구나!

까지만 생각하지 마시고, 우리는 좀더 깊게!!

 

Application 의 Context 는 Singleton 입니다.

즉, 1개의 앱은 1개의 Application Context 만을 갖고 있습니다.

Activity 와 Service 의 Context 는 생성될 때마다 각자의 새로운 Context 를 갖고 있습니다.

 

이해를 돕기 위하여 바로 다음단계를 작성하겠습니다.

LifeCycle 에 따른 사용 가능한 Context

Application Context 와 Activity Context 의 존재시점

위 그림처럼 Context 는 LifeCycle 과 함께 존재합니다.

자, 이제 우리는 getApplicationContext(), getBaseContext(), this 등으 로 아무 생각없이 사용하던 Context 가 어느 곳에 있는 Context 를 사용하고 있는 것인지 생각할 수 있게 되었습니다.

 

Context 를 사용하는 코드로 이 글의 결론에 한발짝 다가서봅니다.

1. Activity 에 선언된 View 의 Context 는 누구일까?

public class MainActivity extends Activity {
    private final String TAG = MainActivity.class.getSimpleName();
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.tvTitle);

        if (textView.getContext() == getApplicationContext()) {
            Log.d(TAG,"Application Context");
        }

        if (textView.getContext() == this) {
            Log.d(TAG,"Activity Context");
        }
    }
}

정답은 Activity Context 입니다.

위 코드처럼 작성한 View 는 Activity Context 를 전달받게 됩니다. 따라서 View 의 Context 는 Activity Context 와 동일합니다.

 

2. CustomView 를 만들어서 xml 에 직접 추가하는 경험을 하신 분들도 계실겁니다. 그렇다면 View 를 직접 만들 땐..?

public class MainActivity extends Activity {
    private final String TAG = MainActivity.class.getSimpleName();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView1 = new TextView(this);
        TextView textView2 = new TextView(getApplicationContext());

        if (textView1.getContext() == getApplicationContext()) {
            Log.d(TAG,"textView1 : Application Context");
        }

        if (textView1.getContext() == this) {
            Log.d(TAG,"textView1 : Activity Context");
        }

        if (textView2.getContext() == getApplicationContext()) {
            Log.d(TAG,"textView2 : Application Context");
        }

        if (textView2.getContext() == this) {
            Log.d(TAG,"textView2 : Activity Context");
        }
    }
}

전달받은 Context 가 View 의 Context 가 됩니다.

이 상황에서 문제가 발생합니다.

현재 코드의 이상향은, MainActivity 가 종료되고 다른 Activity 가 실행된다고 하면, MainActivity 에 있는 객체들은 GC 의 대상이 되어야 합니다.

그러나 textView2 같은 경우는 Application Context 를 사용하여 만들어졌고, Application Context 를 참조하고 있습니다.(실행결과)

 

그렇다면 textView2 는 GC 대상에서 제외됩니다. 이런 경우 메모리 릭이 발생합니다..

*메모리 릭 : 메모리 누수(memory leak) 현상은 컴퓨터 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 현상이다. (wiki)

 

유사 케이스로 아래처럼 View 를 static 으로 선언하여 사용하려는 경우에도 메모리 릭이 발생합니다.

(하지만, 이 경우는 경고 메시지를 통해서 '아 static 쓰지 말라는구나' 하고 지워버리기 때문에 그냥 넘어가는 분들도 계실겁니다.)

 

3. View 만 조심하면 되는데, 그럼 끝난거 아닌가?

 

public class MyClass {
    private static MyClass instance;
    private Context mContext;

    public static MyClass getInstance(Context context) {
        if(instance == null) {
            instance = new MyClass(context);
        }
        return instance;
    }

    private MyClass(Context context) {
        mContext = context;
    }

    public String toString(Context context) {
        StringBuilder sb = new StringBuilder();
        sb.append("MyClass: ");
        if(context == mContext) {
            sb.append("True");
        } else {
            sb.append("false");
        }
        return sb.toString();
    }
}

위와 같이 context 를 전달받는 싱글톤 객체가 있다고 가정하겠습니다.

 

public class MainActivity extends Activity {
    private final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, MyClass.getInstance(this).toString(this));

        startActivity(new Intent(MainActivity.this, NextActivity.class));
        finish();
    }
}

public class NextActivity extends Activity {
    private final String TAG = NextActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, MyClass.getInstance(this).toString(this));
    }
}

그리고 이렇게 실행한 결과를 보도록 하겠습니다.

MainActivity 에서는 MainActivity 의 Context 와 싱글토 객체의 Context 가 일치합니다.

MainActivity 의 Context 를 전달해 주었기 때문이죠..

 

그러나 NextActivity 에서는 false 가 출력됩니다.

NextActivity 의 Context 는 MainActivity 의 Context 와 다르기 때문이죠.

 

그럼 Application Context 랑 비교하면?

저희는 위에서 공부했듯이 당연히 false , false 가 출력될 것이라는 것을 알 수 있습니다.

 

그렇다면, 이 코드의 문제는?

역시나, MainActivity 의 Context 가 사라지지 않는다는 것입니다.

싱글톤 객체는 어짜피 유지되어야 하지 않나요?

우리는 싱글톤 객체가 유지되기를 원한것이지, MainActivity의 Context 까지 유지되기를 원한 것이 아닙니다.

만약 싱글톤 객체에서 Context 의 기능이 필요하다면 Application Context 를 사용하는게 메모리릭을 방지 할 수 있겠죠?

Application Context 는 앱에서 단 한개만 존재하고, 그렇다는 것은 어디서 비교해서 true true 가 나오겠죠?

 

 

오늘의 결론

1. Application Context 는 실행이후 단 1개만 존재하며, Application 의 LifeCycle 과 함께 해야 한다. 

2. Activity Context 는 생성되는 경우에 따라 여러개 존재하며, Activity 의 LifeCycle 과 함께 해야 한다.

3. 만약 Activity LifeCycle 과 함께하는 데이터가 Application Context 를 사용한다면, 그것은 곧 메모리 릭으로 이어진다.

 

* Context 사용시 LifeCycle 을 고려하여 작성하자! 
이 객체는 Activity 에서 사용하니까 Activity Context 를 써야지 라고 생각하면 안된다!!

객체의 LifeCycle 을 고려해야 한다!! Static 변수를 조심하자!!

메모리 릭이 계속 발생한다면, 정상적으로 동작하던 앱을 끄지 않고 이것저것 사용하는 도중에 갑자기 꺼질 수도...

'BackUp (관리중지) > Android 이론' 카테고리의 다른 글

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
[Android Jetpack] LiveData란,  (0) 2021.04.21
Fragment 와 Fragment LifeCycle 분석  (0) 2021.04.19
Activity 와 Activity LifeCycle 분석  (0) 2021.04.15

+ Recent posts