Activity와 Fragment
안드로이드 애플리케이션에서 UI를 구성하려면 Activity 또는 Fragment를 사용한다. 그렇다면 Activity와 Fragment는 어떤 점에서 차이가 나고, 둘 중 어떤 것을 사용해야 할까?
이 글은 안드로이드 스터디에서 작성한 내용을 바탕으로 작성되었으며, 아래 링크에서 발표 자료를 확인할 수 있다.
Activity
안드로이드 애플리케이션의 구성요소(Activity
, BroadcastReceiver
, Service
, ContentProvider
) 중 하나.
일반적으로 하나의 Activity는 하나의 화면을 나타낸다.
사용자가 애플리케이션과 상호작용하기 위한 진입점. 안드로이드 애플리케이션은 콘솔 애플리케이션 처럼 main
함수를 사용하지 않으며, 대신 lifecycle
에 해당하는 함수 마다 수행할 작업을 정의한다. lifecycle
함수는 Android System에 의해 호출된다. 즉, Activity
객체는 Android System에 의해 관리된다.
Activity의 상호작용
안드로이드에서 Activity
사이의 상호작용은 일반적인 코틀린 객체처럼 할 수 없다.
val activity = FooActivity()
foo.doSomething()
Activity
(안드로이드 구성요소) 사이의 상호작용은 Intent
를 사용하여 이루어진다.
Intent: 안드로이드 구성요소 사이의 상호작용(메시지 전달)을 위해 사용되는 메시지 객체.
Context
Activity
와 Fragment
의 차이를 이해하기 위해선 먼저 Context
의 개념을 알 필요가 있다. 아래 이미지는 개발 시 주로 사용하는 AppCompatAcitivty
의 클래스 상속 구조를 나타낸다.
공식 문서에서는 Context
에 대해 다음과 같이 설명한다.
애플리케이션 환경의 전역적인 정보에 대한 인터페이스. Context 클래스 자체는 추상 클래스이며 구현 클래스는 안드로이드 시스템에 의해 제공된다. Context는 애플리케이션의 리소스와 클래스에 대한 접근과, Activity 실행과 Broadcast, intent 수신 등의 애플리케이션 레벨의 명령어를 제공한다.
Activity
가 Context
를 상속하기 때문에 Activity
내에서 getResource
, startActivity
, getSystemService
등의 메소드를 호출할 수 있으며, View(Widget)
또는 Toast
메시지를 생성할 때 Context
파라미터에 this
를 전달할 수 있다.
하지만 Activity
에서는 Context
의 참조를 얻기 위해 this
뿐만 아니라 여러 방법을 제공한다. Activity
에서 Context
를 참조하는 방법은 다음과 같다.
this
ContextWrapper#getBaseContext()
ContextWrapper#getApplicationContext()
안정적인 애플리케이션을 위해선 Context
가 필요한 시점에 적절한 방법을 사용하여 Context
를 제공할 수 있어야 한다. 그렇다면 각각의 메소드를 사용해 얻는 Context
는 어떤 차이가 있을까?
BaseContext
부터 알아보자.
BaseContext
Activity
에서 호출할 수 있는 getBaseContext
메소드는 Context
클래스가 아닌 ContextWrapper
클래스가 제공하는 메소드이다. 따라서 getBaseContext
가 제공하는 Context
를 이해하기 위해선 ContextWrapper
를 봐야 한다.
앞에서 Activity
가 Context
를 상속하기 때문에 시스템 리소스나 메소드를 사용할 수 있다고 말했다. 하지만 실제로는 Activity
또는 그 위로 이어지는 부모 클래스에서 Context
에서 제공하는 메소드들을 구현하지 않는다.
Context
에 대한 구현은 ContextImpl
이라는 클래스에서 제공한다. ContextImpl
은 Context API의 일반적인 구현 방식을 정의한 클래스로, Activity
또는 다른 애플리케이션 구성요소에게 base context object를 제공한다.
ContextWrapper
는 바로 이 ContextImpl
을 래핑하는 클래스로 Context API의 구현을 ContextImpl
에게 위임한다.
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}/** @hide */
@Override
public void startActivityAsUser(Intent intent, UserHandle user) {
mBase.startActivityAsUser(intent, user);
}/** @hide **/
public void startActivityForResult(
String who, Intent intent, int requestCode, Bundle options) {
mBase.startActivityForResult(who, intent, requestCode, options);
}
ContextImpl
객체는 안드로이드 시스템에 의해 생성되며 ContextWrapper
의 생성자 또는 attachBaseContext
메소드에서 mBase
필드에 주입된다. Activity
에서 getBaseContext
를 호출하면 ContextWrapper
의 mBase
가 반환된다.
아래의 다이어그램은 이러한 클래스들의 관계를 보여준다.
ApplicationContext
Context
로서의 Activity
와 getApplicationContext
를 통해 얻을 수 있는 Context
의 차이점을 이해하기 위해선 앞서 말한 Context
의 개념을 조금 확장할 필요가 있다.
Which Context should I use in Android?에 따르면 Context를 다음과 같이 설명한다.
애플리케이션에서 특정 시점의 상태. ApplicationContext는 앱에서 전역적이거나 기본이 되는 base Configuration을 나타내며, Activity에서의 Context는 Activity-specific Configuration을 나타낸다.
Context
가 단순한 인터페이스 뿐만 아니라 상태이며 범위를 가진다는것은 Context
가 일종의 식별자(identifier)로도 사용된다는 의미이다. Activity
에서 View
나 Intent
를 생성할 때 Context
로 this
를 넘겨주는 것은 생성하는 View
나 Intent
가 다른 Context
가 아닌 해당 Activity
에 속한다는 것을 말한다.
따라서 Activity
에서 Context
를 사용하거나 전달할때는 Context
가 사용되는 범위를 생각하고 사용해야 한다. ViewModel
처럼 Activity
보다 생명주기가 긴 객체에서 ActivityContext
를 참조하고 있다면 Activity
가 종료되더라도 여전히 Context
에 대한 참조가 남아있기 때문에 메모리 누수가 발생한다.
Context
사용 목적이 단순히 String
이나 Color
등의 시스템 리소스에 대한 접근이라면 ActivityContext
대신 ApplicationContext
를 전달할 수도 있다.
Toast
의 경우 context
파라미터에 application context
를 전달해도 동작한다. 이는 Toast
가 특정 Activity
의 윈도우에 속하지 않고 자신만의 윈도우를 생성하기 때문이다. — by Android의 Context란?
Fragment
독립적인 라이프사이클과 상태를 가지며 대부분의 경우 UI와 연결된 모듈로, Activity
를 분할할 수 있다.
Fragment
는 독립적으로 존재할 수 없고 Fragment
를 호스팅하는 Activity
나 부모 Fragment
가 필요하다.
대부분의 경우 FragmentManager
에 의해 관리되지만 FragmentManager
로부터 Fragment
인스턴스를 참조하여 직접 조작할 수도 있다.
Activity
보다는 좀 더 일반적인 코틀린 객체에 가깝다.
상호작용
직접 참조
Fragment
내에서 parentFragmentManager
또는 childFragmentManager
를 사용하여 다른 Fragment
인스턴스의 참조를 얻을 수 있다.
val fragmentB = parentFragmentManager.findFragmentByTag("FragB")
as? FragmentBfragmentB?.doSomething()
이러한 방식은 Fragment
를 직접 조작할 수 있지만 권장되진 않는다. 가장 큰 문제는 참조를 얻은 Fragment
의 상태를 모른다는 점이다. FragmentB
의 doSomething
메소드에서 Fragment
의 View
를 변경한다고 했을 때 메소드를 호출하는 시점에서 이미 FragmentB
의 View
가 파괴되었다면 NPE가 발생할 수 있다.
Fragment
에서의 안전한 상호작용을 위해 Fragment 1.3.0 부터 Fragment Result API 가 도입되었다.
Fragment Result API
Fragment 1.3.0-alpha04부터 각 FragmentManager
는 FragmentResultOwner
를 구현한다. 즉, FragmentManager
가 Fragment
에서 이벤트 전달을 위한 모더레이터 역할을 수행할 수 있다.
Fragment Result API를 사용한 상호작용은 Key를 사용한 이벤트 옵저빙 방식으로 동작한다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
parentFragmentManager.setFragmentResultListener("requestKey", this) { requestKey, bundle ->
val result = bundle.getString("bundleKey")
}
}button.setOnClickListener {
val result = "result"
parentFragmentManager.setFragmentResult(
"requestKey", bundleOf("bundleKey" to result)
)
}
Result
수신을 대기중인 Fragment
는 STARTED
상태가 될 때 까지 Result
를 수신하지 않는다. STARTED
상태로 변경되었을 때 리스너에 등록한 콜백을 수행한다.
Context
앞서 Activity
의 계층 구조에서 보았듯 Activity
는 Context
를 상속하기 때문에 Activity == Context
가 가능하다. 반면에 Fragment
는 Context
가 아니다. 대신 Fragment
에서는 Fragment
를 호스팅하는 Activity
의 Context
를 얻을 수 있다.
Fragment
에서 Context
를 얻는 방법은 두 가지가 있다.
getContext()
requireContext()
getContext
메소드와 requireContext
메소드는 반환 객체의 타입이 Nullable
이냐에 따라 구분된다.
requireContext
의 경우 먼저 getContext
로 context
얻은 후 해당 context
가 null
이라면 예외를 던지기 때문에 Fragment
가 호스팅된 이후 사용해야 한다고 말한다.
하지만 실제로 Fragment
의 라이프사이클 콜백마다 requireContext
를 사용해 보면 super.onAttach()
이전이나. super.onDetach()
이후에도 문제없이 사용할 수 있다. 결국 Fragment
의 라이프사이클 내에선 문제 없이 requireContext
를 사용할 수 있다.
내부 코드를 통해 좀 더 자세히 알아보자.
@Nullable
public Context getContext() {
return mHost == null ? null : mHost.getContext();
}
Fragment
의 getContext
메소드는 mHost
의 context
를 반환한다. mHost
는 Fragment
클래스의 필드로 FragmentHostCallback
타입이다.
FragmentHostCallback
은 추상 클래스로, Activity
, Context
, Handler
, FragmentManager
타입의 필드를 가진다.
public abstract class FragmentHostCallback<E>
extends FragmentContainer {
@Nullable private final Activity mActivity;
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
private final int mWindowAnimations;
final FragmentManager mFragmentManager =
new FragmentManagerImpl(); ...
}
Fragment
의 getContext
는 mHost
의 mContext
를 반환하지만, Fragment
객체의 생성 시점에서는 null
로 초기화된다. 그렇다면 mHost
는 언제 초기화되는 것일까?
mHost
가 초기화 되는 코드를 추적해보면 FragmentStateManager
의 attach
, detach
메소드 내에서 객체가 할당되고 해제된다.
class FragmentStateManager {
...
void attach() {
...
mFragment.mHost = mFragment.mFragmentManager.getHost();
mFragment.mParentFragment = mFragment.mFragmentManager.getParent();
mDispatcher.dispatchOnFragmentPreAttached(mFragment, false);
mFragment.performAttach();
mDispatcher.dispatchOnFragmentAttached(mFragment, false);
}
void detach() {
...
mFragment.performDetach();
mDispatcher.dispatchOnFragmentDetached(
mFragment, false);
mFragment.mState = Fragment.INITIALIZING;
mFragment.mHost = null;
...
}
...
}
여기서 주의깊게 봐야할 부분은 mHost
가 초기화되는 코드와 performXXX
메소드가 호출되는 코드의 순서이다.
Fragment
의 onAttach
, onDetach
메소드는 각각 performAttach
, performDetach
메소드 내에서 호출된다. 하지만 mHost
는 performAttach
가 실행되기 전과 performDetach
가 실행된 후에 초기화된다. 따라서 우리는 Fragment
내에서 onAttach
이전과 onDetach
이후에도 문제 없이 requireContext
를 사용할 수 있다.
그렇다면 우리는 언제 requireContext
를 사용해선 안될까? 당연한 말이지만 attach
가 호출되기 이전과 detach
가 호출된 이후이다.
코드를 이렇게 작성할 일은 없겠지만 만약 Fragment
객체가 생성되는 시점에서 필드를 초기화할 때 requireContext
를 사용하거나, Fragment
가 FragmentManager
에 의해 add
또는 replace
되기 전 requireContext
를 사용한다면 예외가 발생할 수 있다.
이에 비해 detach
된 이후 requireContext
를 사용하는 것은 생각보다 가능성 있는 상황이다.
Fragment
에서 네트워킹 또는 비동기 작업을 수행하고 콜백 리스너 내에서 Context
를 사용해야 하는 상황을 예로 들어보자. 작업의 시작은 Fragment
가 호스팅되는 중에 이루어지지만 콜백이 호출되는 시점에도 Fragment
가 여전히 호스팅 중이라고 보장할 순 없다.
이런 경우엔 requireContext
대신 getContext
를 사용하고 null
처리를 하거나, Fragment
가 파괴되는 시점에서 비동기 작업을 취소하는 등의 예외 처리가 필요하다.
Activity vs Fragment
Fragment
는 태블릿 등 다양한 화면 구성에 대응하고 재사용성을 높이기 위해 도입되었다. 하지만 우리는 태블릿용 앱이 아니더라도 단일 Activity
에 여러 개의 Fragment
를 사용하여 화면과 내비게이션을 구성하거나, 단일 Activity
가 아니더라도 흐름상 큰 틀만 Activity
로 구분하고 나머지는 Fragment
를 사용하여 구성하는 방식을 자주 사용한다.
하지만 Fragment
없이 Activity
만 사용하더라도 충분히 애플리케이션을 개발할 수 있다. 복잡한 화면 대응이 필요 없더라도 Fragment
를 사용하는 이유는 무엇일까?
Heavy Activity, Light Fragment
흔히 무거운 Activity
에 비해 Fragment
는 더 가볍다고 말한다. 물론 무겁고 가볍다는 것은 상대적인 기준이기 때문에 Activity
가 무겁다고 한들 시작에 몇 초씩 소요되는것은 아니다.
그렇다면 왜 Fragment
를 더 가볍다고 말하는 것일까?
이 차이를 알기 위해선 앞서 말한 Activity
와 Fragment
의 상호작용 과정을 살펴볼 필요가 있다.
Activity
사이의 상호작용은 항상 시스템이 개입한다. 새로운 Activity
의 생성, 전환, 소멸 등의 과정은 상호작용하는 Activity
를 직접적으로 참조하지 않고 Intent
와 SystemService
등의 중간 과정이 필요하다.
Activity
는 설계 단계부터 다른 프로세스에서 실행하는 것이 염두되었고 따라서 각각의 Activity
는 서로의 메모리를 공유하지 않는다. 이로 인해 커널 레벨에서 프로세스간 통신이 필요해지고 퍼포먼스를 떨어뜨리는 원인이 된다.
이에 비해 Fragment
는 호스팅하는 Activity
내에서 FragmentManager
에 의해 관리된다.
FragmentManager
를 사용하여 직접적인 객체의 참조를 얻을 수도 있고 메모리 영역을 공유할 수도 있다. 따라서 Activity
보다 빠른 상호작용이 가능하다.
또한 Fragment
는 객체 생성 과정에서 시스템의 보조가 필요하지 않다. Activity
처럼 상속 구조와 의존성이 복잡하게 꼬여있지 않기 때문에 좀 더 부담 없이 사용할 수 있다.
Lifecycle
Activity lifecycle
라이프사이클 각각에 대해선 이미 문서에 정리가 잘 되어있기 때문에 이에대한 내용은 생략하고 이 절에서는 특정 상황에서 라이프사이클이 어떻게 변하는지에 대해 소개한다.
다른 Activity를 시작할 때
다른 Activity를 시작하여 기존의 화면이 다른 화면으로 가려진다면 라이프사이클은 어떻게 호출될까?
편의상 기존의 Activity
를 Caller Activity, 새로 시작되는 Activity
를 Callee Activity라고 하자.
라이프사이클 그래프에 따르면 Caller Activity는 onPause
, onStop
이 호출되고 Callee Activity는 onCreate
, onStart
, onResume
이 호출된다. 하지만 이 메소드들이 호출되는 순서는 약간의 차이가 있다.
실제로 로그를 찍어보면 알 수 있듯 Caller Activity의 onPause
이후 Callee Activity의 onCreate
, onStart
, onResume
이 호출되고 Caller Activity의 onStop
이 호출된다.
왜 이런 순서로 호출되는 것일까?
이를 이해하기 위해선 라이프사이클의 onPause
와 onStop
을 확인할 필요가 있다.
Activity
에서 onPause
가 호출되더라도 해당 Activity
는 여전히 화면에서 일부라도 볼 수 있다. Activity
의 전체 화면이 가려져 더 이상 보이지 않을 때 onStop
이 호출된다.
이 말은 Activity
의 onStop
이 호출되려면 새로 떠오르는 화면이 얼마만큼의 영역을 차지하는지를 먼저 알 수 있어야 한다는 것을 의미한다. 따라서 Callee Activity의 onCreate
, onStart
, onResume
까지 호출된 이후 새 화면의 영역이 정해지고 나서야 Caller Activity의 onStop
이 호출된다.
그렇다면 다이얼로그 테마 Activity
나 투명한 Activity
처럼 기존의 Activity
가 일부라도 보이는 경우에는 라이프사이클이 어떤 순서로 호출될까?
앞에서 말했듯 Caller Activity의 onStop
은 호출되지 않는다. 따라서 Caller Activity의 onPause
이후 Caller Activity의 onCreate
, onStart
, onResume
까지만 호출된다.
기존의 Activity로 돌아올 때
백 버튼 등을 눌러 기존의 Activity
로 돌아올 때의 라이프사이클은 다른 Activity
를 시작했을 때 라이프사이클의 반전된 결과를 보여준다.
다른 Activity
를 시작했을 때는 Caller Activity의 onPause
가 먼저 호출되고 Callee Activity의 라이프사이클 이후 Caller Activity의 onStop
이 호출되었지만, Activity를 종료할 때는 Caller와 Callee가 반전되어 Callee Activity의 onPause
가 먼저 호출된 이후 Caller Activity의 onStart
, onResume
이 호출되고 나서야 Callee Activity의 onStop
, onDestroy
가 호출된다.
Fragment lifecycle
Fragment
의 라이프사이클은 기본적으로 호스트의 라이프사이클에 맞춰진다. 즉, 호스트 Activity
가 onResume
까지 호출되었다면 Fragment
도 onResume
까지 호출하며, 홈 버튼을 눌러 Activity
가 onStop
까지 호출된다면 Fragment
또한 onStop
까지 호출된다.
이 때, Activity
의 레이아웃 상에서 Fragment
가 이미 명시되었다면 Fragment
의 라이프사이클은 Activity
라이프사이클 이후 바로 호출된다. 다음 예시는 Fragment
하나를 가지는 Activity
를 시작시킨 후 종료시켰을 때의 호출되는 라이프사이클 콜백을 보여준다. 로그는 각각의 라이프사이클 콜백에서 super.onXXX
메소드 이후 출력된다.
Activity
가 Fragment
를 호스트하므로 시작될 때는 Activity
의 라이프사이클이 먼저 호출되고 종료될 때는 Fragment
의 라이프사이클이 먼저 호출되는 것이 이치에 맞다. 다만 Activity
의 onStart
의 경우 로그 상 Fragment
보다 나중에 호출되는 것 처럼 보이는데 이는 다른 라이프사이클 콜백과 달리 Fragment
의 onCreateView
, onViewCreate
, onStart
등의 콜백이 Activity
의 onStart
가 호출된 이후가 아니라 Activity
의 onStart
콜백 내에서 호출되기 때문에 위와 같은 로그를 보여준다.
Fragment
를 레이아웃에서 명시할 수도 있지만 add/remove
, replace
, attach/detach
, show/hide
등의 트랜잭션을 사용해서 조작할 수도 있다. 각각의 트랜잭션에 따라 Fragment
의 라이프사이클과 상태가 어떻게 변경되는지 확인해보자.
Fragment transactions
add/remove/replace
Fragment
를 FragmentManager
에 추가하기 위해선 add
트랜잭션을 호출한다. 추가된 Fragment
는 onAttach
, onCreate
, onCreatView
, onViewCreated
, onStart
, onResume
순서로 콜백이 호출되며 RESUMED State
가 된다.
콜백 함수에서 Fragment
의 State
를 같이 출력해보면 onResume
에서의 State
가 STARTED
로 나오는 것 처럼 콜백 함수와 State
가 매치되지 않는 경우를 볼 수 있다. 이는 콜백 함수가 완전히 종료된 이후 State
가 변경되기 때문이다. Fragment.FragmentLifecycleCallbacks
처럼 Fragment
의 State
가 변경된 이후 호출되는 콜백에서 로그를 찍어보면 정상적으로 State
가 변경되는 것을 볼 수 있다.
add
메소드를 좀 더 살펴보자.
add
를 사용하는 방식은 크게 두 가지 방법이 있다.
- 실제
Fragment
객체를 넘겨주는 경우 Fragment
의 클래스를 넘겨주는 경우
1번 방법의 경우 미리 생성된 Fragment
객체를 넘겨주기 때문에 Fragment
가 remove
되더라도 Fragment
객체의 필드가 보존된다. 따라서 remove
된 fragment
를 다시 추가하는 경우 기존의 값들이 사용될 수 있다. 다만 이미 추가된 Fragment
를 다시 추가하려 한다면 예외가 발생한다.
2번 방법은 트랜잭션이 수행될 때 마다 FragmentFactory
에서 새로운 Fragment
인스턴스가 생성되어 추가된다. 따라서 Fragment
에 대한 참조를 별도로 관리하지 않는다면 remove
된 Fragment
는 GC 대상이 된다.
추가된 Fragment
를 제거할 때는 remove
트랜잭션을 호출한다. remove
메소드는 실제 Fragment
객체를 파라미터로 받는다. 따라서 외부에서 관리중인 Fragment
객체나 findFragmentByXXX
등의 메소드로 얻은 Fragment
객체를 전달해야 한다.
제거된 Fragment
는 onPause
, onStop
, onViewDestroy
, onDestroy
, onDetach
순서로 콜백이 호출되며 DESTROYED State
가 된다.
NOTE: 만약 remove
를 호출하는 트랜잭션이 백스택에 추가된다면 제거되는 Fragment
는 완전히 제거되지 않고 뷰만 파괴된 채 FragmentManager
에 의해 관리된다.
replace
는 호스트에 attach
되어있는 모든 Fragment
를 remove
하고 교체할 Fragment
를 추가한다. add
와 마찬가지로 replace
메소드에 클래스를 전달하면 트랜잭션이 실행될 때 마다 attach
된 모든 Fragment
를 제거하고 교체할 Fragment
를 새로 생성하여 추가하지만, 이미 attach
된 Fragment
를 대상으로 replace
를 실행하면 기존에 attach
된 Fragment
들만 제거된다.
attach/detach
attach
와 detach
트랜잭션에서 주의해야 할 점은 트랜잭션 이름에 해당하는 라이프사이클 콜백이 있지만 트랜잭션과 Fragment
콜백이 일치하지 않기 때문에 혼란이 있을 수 있다. 따라서 해당 트랜잭션의 이름을 콜백과 일치하게 변경한다면 createView
와 destroyView
라고 말할 수 있다.
attach
와 detach
트랜잭션은 모두 기존에 추가된 Fragment
객체를 대상으로 수행되며, 추가로 attach
트랜잭션의 경우엔 detach
된 Fragment
만을 대상으로 수행된다.
detach
트랜잭션은 Fragment
의 뷰를 파괴한다. detach
된 Fragment
는 onPause
, onStop
, onDestory
순으로 콜백을 호출하며, STOPPED
상태가 된다. 즉, 뷰는 Fragment
내에서 파괴되었더라도 Fragment
자체는 여전히 FragmentManager
에서 관리 대상에 속한다.
attach
트랜잭션은 detach
로 인해 파괴된 뷰를 다시 생성하는 트랜잭션이다. attach
된 Fragment
는 onCreaveView
, onViewCreated
, onStart
, onResume
순으로 콜백을 실행하며, RESUMED
상태가 된다.
여기서 알 수 있는 것은 Fragment
의 라이프사이클과 뷰의 라이프사이클이 다른다는 점이다. Fragment
의 라이프사이클 내에서 뷰는 얼마든지 파괴되었다가 다시 생성될 수 있다. 즉, Fragment
는 뷰보다 긴 라이프사이클을 가진다.
때문에 Fragment
내에서 뷰에 대한 참조를 가지고 있다면 뷰가 파괴되더라도 참조가 존재하기 때문에 GC에 의해 수집되지 않는다. 따라서 Fragment
를 사용할 때는 뷰가 파괴되는 시점에 리소스를 해제하는 작업이 필요하다.
show/hide
show, hide
트랜잭션은 Fragment
의 라이프사이클이나 상태와 관련 없이 추가된 Fragment
의 Visibility
만 변경한다. 따라서 Fragment
와 뷰는 여전히 메모리 내에 존재한다.
참고
- https://developer.android.com/guide/components/activities/intro-activities?hl=ko
- https://developer.android.com/guide/components/intents-filters?hl=ko
- https://www.charlezz.com/?p=1080
- https://medium.com/@ali.muzaffar/which-context-should-i-use-in-android-e3133d00772c
- https://developer.android.com/guide/fragments/communicate#fragment-result
- https://academy.realm.io/kr/posts/michael-yotive-state-of-fragments-2017/
- https://www.charlezz.com/?p=44128
- https://medium.com/박상권의-삽질블로그/지원자-95-가-틀리는-startactivity-라이프사이클-당신도-예외는-아닙니다-ed0947a48d6
- https://github.com/xxv/android-lifecycle
- 안드로이드 프로그래밍 Next Step