Activity 생명주기
4대 컴포넌트 중에 하나인 Activity는 애플리케이션에서 보통 하나 이상의 Activity가 서로 연결된 형태로 구성어서 가장 기본이 되는 구성요소이다.
생명주기 ?
Life + cycle의 합성어, Lifecycle이다. 말 그대로 탄생하고 죽음에 이르기까지의 과정이라고 Activity에 적용한 것이다.
생명주기 호출 | |||
Activty 생성 시 | 화면에서 제거 | 종료 안하고 다른 Activity 실행 | |
![]() |
![]() |
![]() |
![]() |
유튜브 앱 시작 | 유튜브 실행 중 카톡 실행 | 카톡 완벽히 종료 | 유튜브 재 실행 |
youtube > onCreate() youtube > onStart() youtube > onResume() |
youtube > onPause() youtube > onStop() kakao > onCreate() kakao > onStart() kakao > onResume() |
kakao > onPause() kakao > onStop() kakao > onDestroy() youtube > onRestart() youtube > onStart() youtube > on Resume() |
youtube > onRestart() youtube > onStart() youtube > onResume() |
Activity는 실행부터 종료까지 많은 상태 변화를 거치며 상태가 변할 때마다 생명주기 함수가 자동으로 호출된다.
Activity의 상태는 ‘활성 상태’, ‘일시 정지 상태’, ‘비활성 상태’가 있다.
- Activity running(활성 상태)
- 사용자 화면에 보이고 있는 상태로 사용자 이벤트에 반응할 수 있는 상태이다.
- 생성된 Activity는 onCreate() → onStart() → onResume() 함수가 호출되면서 활성상태가 된다.
- 일반적으로 setContentView() 함수를 이용하여 액티비티 화면을 출력하는데, setContentView() 함수가 호출되는 시점이 화면 출력 순간이 아니라, onResume() 함수까지 실행하고 그 후 setContentView() 함수에서 출력한 내용이 화면에 나오는 구조이다. 따라서 onResume()이 함수가 실행되기까지 onCreate(), onStart(), onResume() 안에서 setContentView()를 호출하면 화면에 레이아웃이 출력됩니다.
- pause(일시 정지)
- - Activity가 화면에 보이지만 포커스를 잃은 상태이다.
- 예를 들어 dialog가 있다.
- onPause() 함수가 자동으로 호출된다.
- - Activity가 화면에 보이지만 포커스를 잃은 상태이다.
- stop(비활성 )
- 다른 Activity로 인해 화면이 완전히 가려진 상태이다.
- onPause() → onStop()
- 이후 다시 뒤로가기 등으로 돌아오면
- onRestart() → onStart() → onResume()
하나씩 설명
- onCreate()
- 가장 먼저 호출되는 메서드이다.
- 생명주기에서 단 한 번만 실행된다.
- 필수적으로 구현해야하고 Activity가 생성될 때 호출된다.
정리 : 시스템이 activity를 생성할 때 실행되고 Activity의 전체 생명주기 동안 한 번만 발생해야하는 메서드이다.
- onStart()
- Activity가 화면에 표시되기 직전에 호출된다.
- onCreate()를 호출한 뒤 실행되고 Activity가 사용자에게 표시되기 전에 호출된다.
정리 : Activity가 사용자에게 보여지기 직전에 필요한 초기화 작업을 수행한다.
- onResume()
- Activity가 화면에 보여지는 직후에 호출된다.
- 예를 들어 이벤트나 애니메이션을 시작할 수 있다.
정리 : Activity가 화면의 최상위에 위치하고 사용자와 상호 작용할 수 있는 상태가 될 때 호출된다.
- onPause()
- 다른 Activity가 화면 위로 나타나거나 일부 가려질 때 호출된다.
- 조심해야 할 점은 onPause()가 실행될 때는 아주 잠깐이므로 사용자 데이터 저장, 네트워크 호출, 데이터베이스 처리 등을 실행하면 안된다. 부하가 큰 종료 작업은 onStop() 상태일 때 실행되어야 한다.
- onStop()
- Activity가 다른 Activity에 의해 완전히 가려질 때 호출되는 메서드다.
- 예를 들어, 홈 키를 누르거나 다른 Activity로 이동할 경우가 있다.
정리 : Activity가 더 이상 사용자에게 표시되지 않을 때 호출된다. 완전히 가려서 보이지 않을 때 실행된다.
- onDestroy()
- Activity가 소멸되기 전에 호출된다. 사용자가 Activity를 닫거나 finish()가 호출되어 액티비트 종료될 때이다.
- 여기서는 Activity가 사용하는 리소스를 해제하거나 등의 마무리 작업을 수행할 수 있다.
- onRestart()
- onStop()이 호출 된 이후에 다시 기존 Activity로 돌아오는 경우 호출된다.
- onRestart()가 호출된 이후 이어서 onStart()가 호출된다.
Fragment 생명주기
Activity와 비슷한 콜백 메서드들도 있다.
Fragment에서 최소한으로 구현해야 하는 3개의 생명주기 메서드는
- onCreate()
fragment를 생성할 때 호출한다. fragment가 일시정지 혹은 중단 후 재개되었을 때 유지하고 있어야 하는 것을 여기서 초기화 해야한다.
- onCreateView()
fragment가 자신의 인터페이스를 처음 그리기 위해 호출한다. View를 반환해야 한다.
이 메서드는 fragment의 layout 루트이기 때문에 UI를 제공하지 않는 경우에는 null을 반환하면 된다.
- onPause()
사용자가 프래그먼트를 떠나면 첫번째로 이 메서드를 호출한다. 사용자가 돌아오지 않은 경우도 있으므로 여기에 현재 사용자 세션을 넘어 지속되어야 하는 변경사항을 저장한다.
하나씩 설명
- onAttach()
- fragment가 액티비티에 연결할 때 호출
- 연결 되기 전에 일부 초기화 작업을 수행할 수 있다.
- onCreate()
- 프래그먼트가 액티비티의 호출을 받아 생성한다.
- Bunddle로 액티비티로부터 데이터가 넘어온다.
- fragment의 초기화 작업을 수행할 수 있다.
+ onActivityCreated는 중단 됨
- onCreateView()
- 레이아웃 inflate 담당한다.
- savedInstanceState로 이전 상태에 대한 데이터 제공
- onViewCreated()
- onCreateView()를 통해 반환된 View 객체는 onViewCreated()의 파라미터로 전달 된다.
- 이 때 Lifecycle이 INITIALIZED 상태로 업데이트가 되었다.
- 때문에 View의 초기값 설정, LiveData 옵저빙, RecyclerView, ViewPager2에 사용될 Adapter 세팅은 이 메소드에서 해주는 것이 적절하다
- onViewStateRestored()
- 저장해둔 모든 state 값이 Fragment의 View의 계층 구조에 복원되었을 때 호출된다.
- ex) 체크박스 위젯이 현재 체크되어있는가
- View lifecycle owner : INITIALIZED → CREATED 변경
- onStart()
- 사용자에게 보여질 수 있을 때 호출된다.
- Activity의 onStart() 시점과 유사하다.
- Fragment의 childFragmentManager을 통해 FragmentTransaction을 안전하게 수행할 수 있음
- View lifecycle owner : CREATED → STARTED 변경
- onResume()
- 사용자와 프래그먼트가 상호작용 할 수 있는 상태일 때 호출
- Fragment가 보이는 상태에서 모든 Animator와 Transition 효과가 종료되고, 프래그먼트와 사용자가 상호작용 할 수 있을 때 onResume Callback이 호출된다.
- onPause()
- Fragment가 visible 일 때 onPause()가 호출된다.
- 이 때 Faragment와 View의 Lifecycle이 PAUSED가 아닌 STARTED가 됨
- onStop()
- Fragment가 더 이상 화면에 보여지지 않게 되면 onStop() 콜백 호출되게 된다.
- 부모 액티비티, 프래그먼트가 중단될 때, 상태가 저장될 때 호출
- View와 Lifecycle : STARTED → CREATED
- API 28버전을 기점으로 onSaveInstanceState() 함수와 onStop() 함수 호출 순서가 달라짐, 따라서 onStop()이 FragmentTransaction을 안전하게 수행하는 마지막 지점이 된다.
- onDestoryView()
- 모든 exit animation, transaction이 완료되고 Fragment가 화면으로부터 벗어났을 경우 호출된다.
- view와 lifecycle : CREATED → DESTROYED
- 가비지 컬렉터에 의해 수거될 수 있도록 Fragment View에 대한 모든 참조가 제거되어야 한다.
- getViewLifecycleOwnerLiveData()
- onDestroy()
- Fragment가 제거되거나, FragmentManager가 destroy 됐을 경우, onDestroy() 콜백 함수가 호출
- Fragment Lifecycle의 끝을 알림
- onDetach()
- 프래그먼트가 액티비티로부터 해제되어질 때 호출된다.
FirstFragment 생성 | FirstFragment -> SecondFragment | SecondFragment -> FirstFragment |
> onAttach > onCreate > onCreateView > onViewCreated > onViewStateRestored > onStart > onResume |
First> onPause First> onStop Second> onAttach Second> onCreate Second> onCreateView Second> onViewCreated Second> onViewStateRestored Second> onStart Second> onResume First> onDestroyView |
백버튼으로 돌아갈 경우 Second> onPause Second> onStop First> onCreateView First> onViewCreated First> onViewStateRestored First> onStart First> onResume Second> onDestroyView Second> onDestory Second> onDetach |
Activity Stack
- Activity 스택(Activity Stack)이라는 개념을 사용하여 화면 간 전환을 관리한다.
- Activity 스택은 앱이 실행 중일 때 각 화면(Activity)을 스택(Stack)에 쌓아두고, 이전 화면으로 돌아가거나 새로운 화면을 열 때마다 스택에 새로운 화면을 추가한다.
- 이를 통해 앱이 사용자의 탐색 흐름을 추적하고, 뒤로가기 버튼을 눌렀을 때 이전 화면으로 자연스럽게 돌아갈 수 있도록 한다.
단어 정리
Stack ?
스택은 후입선출(LIFO, Last-In-First-Out)의 구조를 가지며, 새로운 항목이 스택의 맨 위에 추가되고, 가장 최근에 추가된 항목이 가장 먼저 제거됩니다. 따라서 Activity 스택에서는 사용자가 현재 화면에서 뒤로가기 버튼을 누를 때마다 스택의 맨 위에서 가장 최근에 추가된 화면이 제거되고 이전 화면이 보여지는 것이다.
Task ?
Activity 스택은 Task라는 단위로 관리된다.
각 Task는 하나 이상의 Activity로 구성되어 있으며, 백스택(back stack)이라고도 불린다.
앱 아이콘을 눌러 앱을 다시 시작하면, 앱은 해당 Task의 최상위 Activity를 다시 보여준다.
back stack ?
각 Task는 백스택이라는 개념을 가지고 있다.
Activity나 Fragment가 스택(Stack) 자료 구조 형태로 관리되는 메모리 영역을 의미합니다.
사용자가 앱 내에서 다양한 화면을 이동할 때 사용되는 기록을 관리하며, 역순으로 사용자의 이전 동작을 추적한다.
stack을 관리하는 방법으로 크게 2가지가 있다.
1. LaunchMode
2. Intent Flag
1. AndroidManifest의 LaunchMode의 옵션으로 관리
launchMode ?
Activity의 특성을 정의하는데 사용되는 속성이다.
launchMode를 설정함으로써 Activity의 동작 방식을 지정할 수 있다.
1-1) android:launchMode="standard"
- 기본적인 런칭 모드로, 새로운 Activity 인스턴스가 항상 현재 Task의 백스택에 추가된다.
- 즉, Activity를 여러 번 시작할 때마다 각각 새로운 인스턴스가 생성되고 백스택에 추가되는 형식이다.
1-2) android:launchMode="singleTop"
- Activity를 시작할 때, 이미 백스택의 맨 위에 동일한 Activity 인스턴스가 있는 경우에는 새로운 인스턴스를 생성하지 않고 기존의 인스턴스를 재사용한다.
1-3) android:launchMode="singleTask"
- 새로운 Task에 시작되며, 해당 Task에는 단 하나의 인스턴스만 존재한다.
- 이미 Task에 존재하는 경우에는 해당 Task를 먼저 앞으로 이동시키고, 그 위에 새로운 인스턴스를 추가한다.
1-4) android:launchMode="singleInstance"
- 다른 launchMode와는 달리 새로운 Task에 시작된다.
- 따라서 해당 Activity를 시작하는 Task에는 해당 Activity 인스턴스만 포함된다.
- 또한 이 모드를 사용하는 Activity는 다른 Task와 분리되어 있으므로, 다른 Task에 속한 다른 Activity와 독립적으로 작동한다.
- 쉽게 말해, singleTask와 동일하지만 활동은 항상 자체 작업의 단 하나의 유일한 멤버로 위에 다른 Activity를 쌓을 수 없다.
2. Intent Flag로 관리
startActivity()에 전달하는 인텐트 플래그에 아래와 같은 플래그를 주어 스택을 관리한다.
2-1) FLAG_ACTIVITY_NEW_TASK
- singleTask와 유사하며 활동을 새 작업에서 시작한다.
- 이미 실행 중인 작업이 있다면 그 작업을 마지막으로 포그라운드로 이동하고 새 Intent를 수신한다
val intent = Intent(this, SecondActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
2-2) FLAG_ACTIVITY_CLEAR_TOP
- 호출하는 Activity 위에 있는 모든 Activity를 제거한다. 호출하는 Activity가 맨 위에 있지 않으면 새로운 인스턴스를 생성한다.
- 새롭게 부른 Activity가 가장 Top에 있는 Activity면 새 인스턴스가 생성되는 대신 기존 인스턴스가 호출을 수신한다.
- 예를 들어, 현재 애플리케이션의 모든 Activity를 제거하고 Main으로 다시 시작할 때 사용된다.
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
2-3) FLAG_ACTIVITY_SINGLE_TOP
- 호출하는 Activity가 이미 스택의 맨 위에 있는 경우 새로운 인스턴스를 시작하지 않고 기존 인스턴스를 재사용한다.
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
Fragment 데이터 전달 방식
1) bundle과 FragmentManager로 전달
전송하려는 Fragment
//PassBundleFragment는 본인이 전달하고자 하는 Fragment class
val bundle = Bundle()
bundle.putString("key", "value")
val passBundleBFragment = PassBundleBFragment()
passBundleBFragment.arguments = bundle parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container_bundle, PassBundleFragment())
.commit()
받을 Fragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
var result = arguments?.getString("key")
return inflater.inflate(R.layout.fragment_pass_b, container, false)
}
2) Fragment Result API를 이용하여 전달
FragmentManager은 fragment의 결과의 중앙 저장소 역할을 하기 때문에 fragment가 서로 직접 참조할 필요없이 fragment간 통신을 할 수 있다.
프래그먼트와 통신 | Android 개발자 | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 프래그먼트와 통신 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 프래그먼트를 재사용하려면 자체
developer.android.com
공식문서를 통해 실습을 해보았다.

데이터를 보내는 SenderFragment와 받는 ReceiverFragment를 만들었다.
위에 사진처럼 한 Activity위에 2개의 Fragment를 띄운다음
SenderFragment에는 2개의 버튼을 넣고 선택한 버튼에 따라 ReceiverFragment의 TextView값을 바꾸는 예제이다.
//ReceiverFragment
...
class ReceiverFragment : Fragment() {
private lateinit var binding:FragmentReceiverBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentReceiverBinding.inflate(inflater,container,false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setFragmentResultListener("request") { key, bundle ->
bundle.getString("senderKey")?.let{
binding.receiveTextView.text = it
}
}
}
}
//SenderFragment
...
class SenderFragment : Fragment() {
private lateinit var binding:FragmentSenderBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentSenderBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(binding){
aButton.setOnClickListener {
// val bundle = Bundle()
// bundle.putString()
val bundle = bundleOf("senderKey" to binding.aButton.text)
setFragmentResult("request",bundle)
}
bButton.setOnClickListener {
val bundle = bundleOf("senderKey" to binding.bButton.text)
setFragmentResult("request",bundle)
}
}
}
}

setFragmentResult, setFragmentResultListener을 사용해서 key값으로 bundle의 값을 전달
3) Fragment간 shared ViewModel로 전달
ViewModel을 사용하여 데이터를 공유하는 방식
- ViewModel은 여러 fragmen간에 또는 fragment와 호스트 활동 간에 데이터를 공유해야 할 때 적합하다.
- Fragment가 Activity 하위에 위치하기 때문에 사용할 수 있다.
Activity를 공유하는 여러 Fragment들은 Activity의 메모리를 공유할 수 있고 AAC의 ViewModel은 Activity의 lifecycle보다 오래 살아있는 것이 보장되기 때문에 안전하게 공통의 Activity의 ViewModel을 사용하여 데이터 전달이 가능하다.
Activity에 Fragment들이 같은 데이터를 바라볼 수 있게 ViewModel 객체를 만들고 그 객체에 데이터를 전달하여 간단하게 Fragment 통신을 달성할 수 있다.
4) Jetpack의 Navigation에서 제공하는 safe-args로 전달
Jetpack의 Navigation을 사용하는 방식이다.
navigation에서 화면전환에 필요한 Action과 data를 정의할 수 있고, defaultValue도 정의가 가능하다.
간결하고 안정적인 처리의 장점이 있지만 다른 Data전달 방식보다 환경세팅하는 것이 많고 알아야 할 기능들이 많다.
직접 실습을 진행해보았다.
초기 설정
1. 2개의 Fragment생성, activity_main.xml 설정
(bottomnavigation은 개인적)

2. navigation 생성

데이터 이동
1. gradle
앱 수준의 gradle
plugins{
...
id("androidx.navigation.safeargs.kotlin")
}
프로젝트 수준의 gradle
plugins {
...
id("androidx.navigation.safeargs.kotlin") version "2.7.7" apply false
}
2. 초기에서 만든 navigation에서 action과 argument를 추가해준다.

<fragment
android:id="@+id/AFragment"
android:name="com.example.kotlin_navi_bottom.AFragment"
android:label="fragment_a"
tools:layout="@layout/fragment_a">
<action //추가
android:id="@+id/action_AFragment_to_BFragment"
app:destination="@id/BFragment" />
</fragment>
<fragment
android:id="@+id/BFragment"
android:name="com.example.kotlin_navi_bottom.BFragment"
android:label="fragment_b"
tools:layout="@layout/fragment_b">
<argument //추가
android:name="aData"
android:defaultValue="AA"
app:argType="string" />
</fragment>

GUI로도 할 수도 있다.
3. MianActivity.kt
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
binding.bottomNavigationView.setupWithNavController(navHostFragment.navController
nav_graph와 연결해주는 코드로 공식문서에 나와있다.
4. 데이터 처리
나는 AFragment의 데이터를 BFragment로 보낼 것이다.
그러면 AFragment의 fragmentDirection은 AFragmentDirections가 된다.
class AFragment : Fragment() {
private lateinit var binding: FragmentABinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentABinding.inflate(inflater, container, false)
binding.aButton.setOnClickListener {
val action = AFragmentDirections.actionAFragmentToBFragment(aData = "This is 'A'data")//
findNavController().navigate(action)
}
return binding.root
}
}
navigation의 argument name을 aData로 지정했었는데 그 aData의 arguments "This is 'A'data"를 전달해 준다.
class BFragment : Fragment() {
private lateinit var binding: FragmentBBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentBBinding.inflate(inflater, container, false)
val aData = BFragmentArgs.fromBundle(requireArguments()).aData
binding.bTextView.text = aData
return binding.root
}
}
결과 : A_Fragment의 버튼을 누르면 B_Fragment의 text가 argument에서 설정한 이름인 aData를 통해서 "This is 'A'Data"라는 Data로 바뀌게 된다.
전달 방식만 보려고 대충 만든 것이다.(내가 봐도 대충만든 것같아서...)
화면이 이동하는 이유는 action을 넣어줬기 때문이다.

ref
[Android] Fragment간 데이터 전달 방법들
Fragment간 데이터 전달에 관한 여러 방법들
velog.io
Jetpack Navigation - 5 (Safe Args)
개복치개발자 강의는 아래의 링크에서 확인할 수 있습니다. 개복치개발자 | Linktree uyalae@naver.com linktr.ee Navigation에서 Fragment끼리 데이터를 전달하는 방법에 대해 Bundle에 대해서 알아봤습니다. 그
philosopher-chan.tistory.com
프래그먼트와 통신 | Android 개발자 | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 프래그먼트와 통신 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 프래그먼트를 재사용하려면 자체
developer.android.com
대상 간 데이터 전달 | Android 개발자 | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 대상 간 데이터 전달 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 탐색을 사용하면 대상 인수를 정
developer.android.com
'Android Studio' 카테고리의 다른 글
Android - TabLayout 사용해서 Fragment 전환 (0) | 2024.03.26 |
---|---|
Android - SharedPreferences (0) | 2024.03.25 |
Android - Firebase Storage에 사진 올리기 (1/2) (1) | 2024.03.15 |
Android - FireStore 규칙 설정 / 데이터 가져오기 (1) | 2024.03.12 |
Kotlin - Firebase Auth로 간단한 로그인 구현 (1) | 2024.03.08 |