728x90
반응형
✅ 들어가기 전에
- 안드로이드에서 앱의 완성도와 안정성을 높이기 위해 Activity Lifecycle은 반드시 알아야 한다.
- 제대로 알지 못하고 앱을 개발하게 되면 다른 앱을 전환하거나, 비정상 종료되는 문제가 발생할 수도 있다.
- 또한, 사용자가 앱을 사용하지 않는데 시스템 리소스가 소비되는 문제를 일으킬 수 있다.
- 사용자가 앱을 나갔다 돌아왔을 때, 진행상태가 저장되지 않는 문제가 발생할 수 있다.
- 화면이 가로 ↔️ 세로 전환될 때, 비정상 종료되거나, 진행상태가 저장되지 않는 문제가 발생할 수 있다.
- 이러한 여러 문제를 일으킬 수 있기 때문에 안드로이드에서 Activity Lifecycle에 대한 숙지는 필수라 할 수 있다.
- 그럼 이제부터 안드로이드의 Activity Lifecycle에 대해서 알아보자.
✅ Activity Lifecycle
- Activity는 아래 그림과 같은 생명주기를 가지고 있다.
- 아래 그림을 바탕으로 Activity 콜백 함수들에 대해 알아보자
📌 onCreate
- onCreate는 필수적으로 구현해야하는 콜백 함수이다.
- 안드로이드 스튜디오에서 Activity 생성 시 반드시 함께 생성된다.
- Activity의 생명주기 중 한 번만 발생해야하는 로직을 실행한다.
- 예를 들어 멤버 변수 정의, UI 구성(setContentView, xml 레이아웃 파일 정의) 등이 있다.
- saveInstanceState 매개 변수(Activity 이전 저장 상태가 포함된 Bundle 객체)를 수신한다.
- 이전 저장 상태를 수신하기 때문에 Activity가 최초로 한 번만 실행했을 때는 값이 null이다.(이전 저장 상태가 없기 때문)
- onCreate가 불리면 onStart가 불리고 그 다음 onResume이 순서대로 불리게 된다.
📌 onStart
- Activity가 사용자에게 표시하기 위해 준비하고 있는 상태
- 앱은 Activity를 포그라운드로 보내 상호작용할 수 있도록 준비한다.
- 찰나의 순간
📌 onResume
- Activity가 포그라운드에 표시되어, 사용자와 상호 작용할 수 있는 상태이다.
- 앱에서 포커스가 떠날 때까지 onResuma 상태에 머무른다.
📌 onPause
- 사용자가 활동을 떠나는 첫 번째 신호로 매우 짧은 순간이다.
- 활동이 포그라운드에 있지 않지만, 잠시 후 다시 시작할 작업을 일시 중지 하거나 조정한다.
- 예를 들어 반투명 Activity가 띄워져 포커스는 없지만 화면에 보이는 상태
- 이 상태를 통해 실행중이지 않을 때 필요하지 않은 리소스를 해지할 수 있다.
- 이 상태에서 데이터를 저장하거나 네트워크 호출, DB의 IO 작업을 하면 안된다.
- 매우 짧은 순간이기 때문에 메서드가 끝나기 전에 Activity가 종료될 수 있다.
📌 onStop
- Activity가 사용자에게 더 이상 표시되지 않는 상태를 말한다.
- 예를 들어 앱을 사용하다가 홈 화면으로 갔을 때 Activity는 보이는 상태가 아니기 때문에 onPause를 거쳐 onStop 상태에 머무르게 된다.
- CPU를 비교적 많이 소모하는 작업을 실행해야한다
- 예를 들면 DB 저장 작업이 있다.
- Activity가 중단되면, Android OS에서 리소스 관리를 위해, 해당 Activity가 포함된 프로세스를 소멸시킬 수 있다.
- onStop 상태에서 프로세스를 kill 하지 않고 다시 실행하게 되면 onRestart, onStart, onResume 상태를 호출하게 되며, 만약 프로세스까지 kill 된 경우 다시 onCreate 부터 실행된다.
📌 onDestroy
- Activity가 완전히 종료되기 전에 실행된다.
- 호출되는 케이스는 다음과 같다.
- finish가 명시적으로 호출되어 Activity가 종료될 때
- configurationChange(ex 기기 회전, 멀티 윈도우)로 인해, 시스템이 Activity를 일시적으로 소멸시킬 때
📌 onRestart
- 액티비티가 멈췄다가 다시 시작되기 바로 전에 호출된다.
✅ 실습
❗️ MainActivity
package com.example.test
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
/**
* 1. Activity 가 실행, 종료 생명주기
* 2. 다른 앱 실행 시, 생명주기 (홈버튼)
* 3. 다른 Activity 실행 시, 생명주기
* */
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.button).setOnClickListener { navigateNextActivity() }
findViewById<Button>(R.id.button2).setOnClickListener { finish() }
Toast.makeText(this, "라이프사이클 - Main : onCreate", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onCreate")
}
override fun onStart() {
super.onStart()
Toast.makeText(this, "라이프사이클 - Main : onStart", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onStart")
}
override fun onRestart() {
super.onRestart()
Toast.makeText(this, "라이프사이클 - Main : onRestart", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onRestart")
}
override fun onResume() {
super.onResume()
Toast.makeText(this, "라이프사이클 - Main : onResume", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onResume")
}
override fun onPause() {
super.onPause()
Toast.makeText(this, "라이프사이클 - Main : onPause", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onPause")
}
override fun onStop() {
super.onStop()
Toast.makeText(this, "라이프사이클 - Main : onStop", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onStop")
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(this, "라이프사이클 - Main : onDestroy", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Main", "onDestroy")
}
private fun navigateNextActivity() {
val intent = Intent(this, NextActivity::class.java)
startActivity(intent)
}
}
❗️ NextActivity
package com.example.test
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
class NextActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_next)
Toast.makeText(this, "라이프사이클 - Next : onCreate", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onCreate")
}
override fun onStart() {
super.onStart()
Toast.makeText(this, "라이프사이클 - Next : onStart", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onStart")
}
override fun onRestart() {
super.onRestart()
Toast.makeText(this, "라이프사이클 - Next : onRestart", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onRestart")
}
override fun onResume() {
super.onResume()
Toast.makeText(this, "라이프사이클 - Next : onResume", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onResume")
}
override fun onPause() {
super.onPause()
Toast.makeText(this, "라이프사이클 - Next : onPause", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onPause")
}
override fun onStop() {
super.onStop()
Toast.makeText(this, "라이프사이클 - Next : onStop", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onStop")
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(this, "라이프사이클 - Next : onDestroy", Toast.LENGTH_SHORT).show()
Log.d("라이프사이클 - Next", "onDestroy")
}
}
❗️ 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/button"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="다른 액티비티 실행"
android:layout_marginTop="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="액티비티 종료"
android:layout_marginTop="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />
</androidx.constraintlayout.widget.ConstraintLayout>
❗️activity_next.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=".NextActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
🔈 앱이 막 실행됐을 때의 Lifecycle
- onCreate, onStart, onResume 순으로 호출되어 onResume 상태에 머무르는 것을 알 수 있다.
🔈 액티비티 종료 버튼을 눌렀을 경우의 Lifecycle
- onResume 다음으로 onPause, onStop, onDestory 순으로 호출되는 것을 볼 수 있다.
- 앱의 Activity가 오직 하나만 호출되어 있었기 때문에 onDestroy까지 호출된 것을 볼 수 있다.
🔈 홈버튼을 누르고 다시 돌아갈 경우의 Lifecycle
- 앱을 실행하고 홈 버튼을 누를 경우 onResume 다음으로 onPause, onStop이 호출 된 것을 볼 수 있다.
- 이때, Activity는 아직 살아있기 때문에 앱을 재실행할 경우 onRestart, onStart가 호출되고 마지막으로 onResume이 다시 호출되는 것을 확인할 수 있다.
🔈 다른 액티비티를 실행했을 경우의 Lifecycle
- 메인 Activity에서 다른 Activity를 호출할 경우 메인 Activity는 먼저 onPause를 호출하는 것을 볼 수 있다.
- 그 다음 호출한 Activity가 실행되면서 onCreate, onStart, onResume을 호출한다.
- 호출된 Activity에서 onResume이 호출되고 나면 메인 Activity에서는 onStop이 호출되는 것을 볼 수 있다.
🔈 다른 액티비티에서 메인 Activity로 돌아가는 경우의 Lifecycle
- 다른 액티비티에서 메인 Activity로 돌아가기 위에 이전 버튼을 누르게 되면 제일 먼저 다른 Activity에서 onPause가 호출되는 것을 볼 수 있다.
- 그 다음 다시 메인 Activity가 실행되면서 onRestart, onStart, onResume이 순차적으로 호출된다.
- 최종적으로 이전에 있던 다른 Activity는 onStop을 호출하게 되고 마지막으로 onDestroy를 호출하여 Activity를 종료하는 것을 볼 수 있다.
📢 Activity Lifecycle은 앱의 완성도 안전성을 위해 반드시 알아야하기 때문에 충분히 연습하면서 숙지하는 것이 필요할 것 같다. 📢
728x90
반응형
'Studying > Android' 카테고리의 다른 글
[Android] 안드로이드 디자인 패턴(2) - MVP 패턴 (0) | 2023.08.02 |
---|---|
[Android] 안드로이드 디자인 패턴(1) - MVC 패턴 (0) | 2023.08.02 |
[Android] 안드로이드 스튜디오 Room을 사용하여 로컬 데이터베이스에 데이터 저장하기 (0) | 2023.05.18 |
[Android] Android 기본 요소 - 4대 컴포넌트 (0) | 2023.05.15 |