이전 글에서 액티비티 생명주기를 알아보았습니다.
각 버튼으로 프래그먼트를 프레임레이아웃에 프래그먼트를 띄우는 예제를 통해서 프래그먼트의 생명주기에 대해 알아보려고 합니다.
2024.11.08 - [안드로이드] - [Android] Activity Lifecycle
[Android] Activity Lifecycle
안드로이드를 공부할 때 가장 먼저 공부하게 되는 것 중에 하나가 생명주기입니다.안드로이드 4대 컴포넌트 중 하나인 액티비티의 생명주기에 관해서 적어보려고 합니다.차근차근 알아보겠습
goharry.tistory.com
프래그먼트 생명주기(Fragment Lifecycle)
프래그먼트 생명주기의 종류는 액티비티의 생명주기 종류보다 많습니다.
간단하게 알아보겠습니다.
onAttach()
- 프래그먼트가 액티비티에 연결될 때 호출
- 액티비티에서 데이터를 가져오는 작업 등을 할 수 있음
onCreate()
- 프래그먼트가 생성될 때 호출
- Bunddle로 액티비티로부터 데이터가 넘어옴
- 데이터의 초기화나 상태복원 등을 처리할 수 있음
- FragmentView가 생성되지 않아서 UI 관련 작업을 하기에 적절하지 않음.
onCreateView()
- 프래그먼트의 레이아웃을 인플레이트하고, 뷰를 반환하는 메서드
- 뷰의 초기화가 여기서 이루어짐
- 프래그먼트의 UI를 설정하는 데 사용
onViewCreated()
- onCreateView를 통해 반환된 View 객체가 onViewCreated()의 파라미터로 전달
- View 관련 초기화 작업을 하기 적절한 시점
onViewStateRestored()
- 프래그먼트의 상태가 복원될 때 호출
- 사용자가 입력한 텍스트, 스크롤 위치 등 뷰에 관련된 상태를 저장하고 복원하는 데 사용
onStart()
- 프래그먼트가 화면에 보이기 직전에 호출
- UI 업데이트 등을 처리
onResume()
- 프래그먼트가 화면에 완전히 표시되고, 사용자가 상호작용할 수 있는 상태일 때 호출
onPause()
- 프래그먼트가 화면에 안보이기 직전에 호출
- 프래그먼트가 다른 액티비티나 프래그먼트로 전환될 때 발생
- 화면이 보이지 않게 되기 전에 데이터를 저장하거나 애니메이션을 중지하는 등의 작업 처리 가능
onStop()
- 프래그먼트의 화면이 더이상 보이지 않게 되었을 때 호출
- 리소스 해제 등의 작업을 할 수 있음
onSaveInstanceState()
- 프래그먼트가 종료되거나 화면 회전 등의 이유로 상태가 저장될 때 호출
- 프래그먼트 상태를 저장하여 나중에 복원할 수 있도록 함
onDestroyView()
- 프래그먼트의 뷰가 파괴될 때 호출(프래그먼트 자체는 아직 존재)
- 예를 들어, 리사이클러뷰의 어댑터를 해제할 때 사용
onDestroy()
- 프래그먼트가 완전히 파괴될 때 호출 (프래그먼트 자체가 더 이상 사용되지 않을 때)
- 비동기 작업 등을 해제할 때 용이
onDetach()
- 프래그먼트가 액티비티와 연결이 끊어질 때 호출
생명주기 확인하기 위한 코드
각 생명주기에 해당하는 메서드들에 Log를 찍어서 확인해 보겠습니다.
MainActivity.class
package abled.semina.lifecycle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private String TAG = "생명주기 확인";
Button buttonMoveSubActivity, buttonMoveAFragment, buttonMoveBFragment;
private final int Fragment_A = 1;
private final int Fragment_B = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonMoveSubActivity = findViewById(R.id.buttonMoveSubActivity);
buttonMoveAFragment = findViewById(R.id.buttonMoveAFragment);
buttonMoveBFragment = findViewById(R.id.buttonMoveBFragment);
Intent moveToSubActivityIntent = new Intent(this, SubActivity.class);
buttonMoveSubActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(moveToSubActivityIntent);
}
});
Log.d(TAG, "onCreate: MainActivity");
buttonMoveAFragment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FragmentView(Fragment_A);
}
});
buttonMoveBFragment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FragmentView(Fragment_B);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: MainActivity");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: MainActivity");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause: MainActivity");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop: MainActivity");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: MainActivity");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart: MainActivity");
}
private void FragmentView(int fragment){
//FragmentTransactiom를 이용해 프래그먼트를 사용합니다.
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (fragment){
case 1:
// 첫번 째 프래그먼트 호출
AFragment aFragment = new AFragment();
transaction.replace(R.id.frameLayout, aFragment);
transaction.commit();
break;
case 2:
// 두번 째 프래그먼트 호출
BFragment bFragment = new BFragment();
transaction.replace(R.id.frameLayout, bFragment);
transaction.commit();
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/buttonMoveSubActivity"
android:layout_width="0dp"
android:layout_height="100dp"
android:text="서브액티비티로 이동"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/buttonMoveAFragment"
app:layout_constraintBottom_toTopOf="@id/frameLayout"
/>
<Button
android:id="@+id/buttonMoveAFragment"
android:layout_width="0dp"
android:layout_height="100dp"
android:text="A프래그먼트로 이동"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/buttonMoveSubActivity"
app:layout_constraintRight_toLeftOf="@id/buttonMoveBFragment"
app:layout_constraintBottom_toTopOf="@id/frameLayout"
/>
<Button
android:id="@+id/buttonMoveBFragment"
android:layout_width="0dp"
android:layout_height="100dp"
android:text="B프래그먼트로 이동"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/buttonMoveAFragment"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/frameLayout"
/>
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="710dp"
android:layout_weight="10"
app:layout_constraintTop_toBottomOf="@id/buttonMoveSubActivity"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
AFragment.class
package abled.semina.lifecycle;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class AFragment extends Fragment {
private final String TAG = "생명주기 확인";
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: A_Fragment");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: A_Fragment");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: A_Fragment");
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "onViewCreated: A_Fragment");
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
Log.d(TAG, "onViewStateRestored: A_Fragment");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: A_Fragment");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: A_Fragment");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause: A_Fragment");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop: A_Fragment");
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState: A_Fragment");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView: A_Fragment");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: A_Fragment");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach: A_Fragment");
}
}
fragment_a.xml
텍스트 뷰에 A Fragment 입니다 띄우는 간단한 코드라 첨부하지 않겠습니다.
BFragment.class
package abled.semina.lifecycle;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BFragment extends Fragment {
private final String TAG = "생명주기 확인";
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: B_Fragment");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: B_Fragment");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: B_Fragment");
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_b, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "onViewCreated: B_Fragment");
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
Log.d(TAG, "onViewStateRestored: B_Fragment");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: B_Fragment");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: B_Fragment");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause: B_Fragment");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop: B_Fragment");
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState: B_Fragment");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView: B_Fragment");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: B_Fragment");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach: B_Fragment");
}
}
fragment_b.xml
텍스트 뷰에 B Fragment 입니다 띄우는 간단한 코드라 첨부하지 않겠습니다.
ADD, REPLACE
add
아무것도 지우지 않는다. 단순히 Fragment를 추가한다.
replace
컨테이너에 추가된 기존 fragment를 교체한다. 현재 추가되어 있는 같은 containerViewId의 fragment를 remove 하고, 새로 추가한다.
즉, 이미 안에 있는 Fragment를 지우고 추가 한다.
ADD
A 프래그먼트 버튼과 B프래그먼트 버튼을 순차적으로 클릭
A프래그먼트가 onAttach부터 onResume까지 호출된 후 B 프래그먼트의 onAttach부터 onResume까지 호출된다.
이 상태에서 시스템 뒤로 가기를 하고 완전히 종료시켰을 때
같은 화면에 동시에 존재하기 때문에 생명주기도 A와 B 동시에 호출되는 것을 확인할 수 있습니다.
Replace
A 프래그먼트 버튼과 B프래그먼트 버튼을 순차적으로 클릭
Add때와 달리 컨테이너에도 한 개의 프래그먼트만 존재한다.
1. A프래그먼트로 이동버튼을 클릭하면
A의 생명주기 함수들이 onAttach부터 onResume까지 순차적으로 호출
2. 그 후 B프래그먼트로 이동버튼을 클릭하면
A의 onPause, onStop이 호출 > B의 onAttach부터 onStart까지 호출 > A의 onDestoryView, onDestroy, onDetach 호출
> B의 onResume 호출
3. 뒤로 가기 후 앱 종료
마지막으로 B의 onPause부터 onDetach까지 순차적으로 호출
4. A 프래그먼트 실행 후 화면 회전하면?!
순서정도만 확인해보겠습니다.
외우는 건 의미가 없는 것 같고 정리만 좀 해보면
메인 액티비티에서 A 프래그먼트를 실행하는 것은 당연히 순서가 똑같습니다.
화면을 회전했을 때 액티비티도 onDestroy까지 호출이되고 새로 onCreate부터 onResume까지 다시 호출됐었는데
프래그먼트가 같이 있다면 이렇게 나오게 됩니다.
회전이 되면 프래그먼트와 액티비티 순서로 파괴되고 재생성되는 느낌입니다.
- onPause : 프래그먼트 > 액티비티
- onStop : 프래그먼트 > 액티비티
- 프래그먼트가 액티비티에서 떨어지고 난 뒤( Fragment의 onDetach까지 호출)
- 액티비티 onDestroy
- 프래그먼트의 onAttach onCreate
- 액티비티 onCreate
- 프래그먼트 onCreateView onViewCreated onViewStateRestored onStart
- 액티비티 onStart
- 프래그먼트 onResume
- 액티비티 onResume
마지막만 액티비티가 먼저 onResume을 호출하고 프래그먼트가 onResume을 호출한다.화면에 보여지는게 액티비티 안에 프래그먼트 컨테이너가 있어서 그런가 싶다.나머지는 비슷한 단계일때 프래그먼트가 먼저 호출된다.
프래그먼트는 생명주기가 액티비티에 비해 훨씬 많아서 역시 직접 확인해 보길 잘한 것 같다.
끝!
'안드로이드' 카테고리의 다른 글
[Android] Application Not Responding (ANR) (2) | 2024.11.12 |
---|---|
[Android] 안드로이드 앱으로 아두이노 제어하기 (USB 시리얼 통신) (7) | 2024.11.10 |
[Android] Activity Lifecycle (1) | 2024.11.08 |
[Android] 기본 스플래시 없애기 (0) | 2024.08.21 |
[Android] MVVM 패턴 예제! (1) | 2024.08.18 |