[4대 컴포넌트] Activity

dwjeong·2023년 9월 20일
0

안드로이드

목록 보기
5/28

🔎 액티비티란

사용자가 앱과 상호 작용하기 위한 시작점. 사용자 인터페이스가 있는 단일 화면을 뜻함.
각각의 액티비티는 함께 작동하여 일관된 사용자 경험을 형성하지만, 각 액티비티는 다른 액티비티와 독립적임.

  • 데스크탑 환경과 다른 점
    사용자와 앱의 상호 작용이 항상 같은 위치에서 시작되지 않음.
    한 앱이 다른 앱을 호출하면 다른 앱의 액티비티를 호출함.

앱에서 액티비티를 사용하기 위해서는 매니페스트에 정보를 등록해야함. 또한 생명 주기를 적절히 관리해야 함.

//매니페스트에 액티비티 등록
<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

⭐ 액티비티 생명주기

onCreate()

시스템에서 액티비티를 만들때는 이 콜백을 구현해야 함.
액티비티가 생성되면 액티비티는 Created 상태가 됨.
onCreate()에서 필수 구성 요소를 초기화해야 함. (예: 뷰를 생성하고 데이터 목록에 바인딩해야 함.
또한 setContentView()를 호출해 사용자 인터페이스에 대한 레이아웃을 정의해야 함.)

액티비티의 이전에 저장된 상태가 포함된 Bundle 객체인 saveInstanceState 매개변수를 받음. 액티비티가 이전에 실행된적이 없을 경우 Bundle 객체의 값은 null이 됨.


//이전 상태 복원은 onCreate() 또는 onRestoreInstanceState()에서 할 수 있음.
//onRestoreInstanceState()는 onCreate()호출 직후 호출됨.
//onRestoreInstanceState()와 onSaveInstanceState()는 생명주기 메서드가 아님.

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// 이전 상태 저장
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState)
}

액티비티 라이프 사이클에 연결된 lifecycle-aware 컴포넌트가 있을 경우 해당 컴포넌트는 ON_CREATE 이벤트를 수신함.

액티비티는 Created 상태로 유지되지 않으며 onCreate() 메서드의 실행이 완료되면 Started 상태가 시작되고 onStart()onResume() 메서드를 빠르게 연속적으로 호출함.

이 다음 콜백은 항상 onStart()임.


onStart()

onCreate()가 종료되면 액티비티가 Started 상태가 되고 액티비티가 사용자에게 표시됨.
onStart()에는 액티비티가 전면에 표시되고 상호작용을 할 수 있도록 하기 위한 최종 준비 작업이 실행됨.
lifecycle-aware 컴포넌트와 연결된 경우 해당 컴포넌트는 ON_START 이벤트를 수신.
onStart() 메서드는 빠르게 완료되며 Resumed 상태가 되고 onResume() 메서드를 호출.


onResume()

시스템은 액티비티와 유저가 상호작용을 시작하기 직전에 이 콜백을 호출하며 사용자와 상호작용하는 상태가 됨.

이 시점에 액티비티는 액티비티 스택 맨 위에 있으며 다른 액티비티로 이동하거나 화면이 꺼지는 등 앱에서 포커스를 뺏기는 일이 발생할 때까지 이 상태를 유지.
앱의 핵심 기능 대부분이 이 메서드에서 구현됨.

Resumed 상태가 되면 연결된 lifecycle-aware 컴포넌트가 ON_RESUME 이벤트를 수신함.


onPause()

항상 onResume()뒤에 오는 콜백 메서드.
액티비티가 포커스를 잃고 일시 중지 상태가 되면 호출됨. 예를 들어 뒤로 가기 버튼이나 최근 활동 보기 버튼을 탭할 때 발생.

액티비티가 곧 중지 혹은 재개 상태가 될 것임을 나타냄.
(즉, 액티비티가 삭제된다는 의미가 아님. 예를 들어 다이얼로그가 열릴 경우 onPause()가 호출될 수 있으며 멀티 윈도우 모드일 경우 하나의 앱에만 포커스가 있으므로 다른 앱들은 일시 중지상태가 됨.)


일시 중지될 경우 사용자가 UI 업데이트를 기대하는 경우 UI를 계속 업데이트할 수 있음.
(예: 내비게이션, 미디어 플레이어 재생)

실행 시간이 매우 짧기 때문에 저장 작업을 수행하기에 충분한 시간을 제공하지 못함.
따라서 이 메서드에서 데이터 저장, 네트워크 호출. 데이터베이스 트랜잭션을 실행해서는 안됨.

연결된 lifecycle-aware 컴포넌트가 ON_PAUSE 이벤트를 수신함.
이 메서드 다음 콜백은 onStop()이 될 수도 있고 onResume()이 될 수도 있음.


onStop()

액티비티가 더 이상 유저에게 표시되지 않을 때 호출됨. 액티비티가 삭제되거나 새 액티비티가 시작되거나 기존 액티비티가 재개될 때 중지된 액티비티를 덮고 있기 때문에 발생.

액티비티가 Stopped 상태가 되며, lifecycle-aware 컴포넌트가 연결된 경우 ON_STOP이벤트를 수신함.

사용자에게 앱이 표시되지 않는 동안 필요하지 않은 리소스를 해제 또는 조정함.
상대적으로 CPU를 많이 사용하는 종료 작업을 수행할 때 이 메서드에서 수행하는 것이 좋음.
(데이터베이스에 정보를 저장할만한 적절한 시기를 찾을 수 없을 경우 onStop()에서 저장 가능.)

이 메서드 다음 콜백은 다시 상호작용하기 위해 유저가 돌아오는 경우 onRestart(),
완전히 종료되는 경우 onDestroy()임.

※ 액티비티 중지 후 시스템에서 메모리를 복구해야 하는 경우 액티비티가 포함된 프로세스를 시스템에서 종료시킬 수 있음. 프로세스가 파괴되더라도 시스템은 key-value 쌍의 Blob인 Bundle에서 EditText 위젯의 텍스트와 같은 개체의 상태를 유지하며 유저가 액티비티로 돌아오는 경우 복원함.


onRestart()

시스템이 중지된 상태의 액티비티를 다시 시작하기 위해 호출하는 콜백 메서드.
액티비티가 중지된 시점의 상태로 복원함.

이 메서드 뒤에는 항상 onStart()가 호출됨.


onDestroy()

시스템이 액티비티가 소멸되기 전에 호출하는 콜백 메서드.

유저가 액티비티를 완전히 종료했거나 액티비티에서 finish()가 호출되어 종료되는 경우,
기기 화면 회전 또는 멀티 윈도우 모드 진입과 같은 구성 변경으로 인해 시스템이 액티비티를 일시적으로 파괴하는 경우 콜백이 호출됨.

액티비티가 수신하는 마지막 콜백이며, 액티비티나 액티비티가 포함된 프로세스가 소멸될 때 액티비티의 모든 리소스가 해제되도록 하기 위해 구현.

lifecycle-aware 컴포넌트가 연결된 경우 ON_DESTROY이벤트를 수신함.

onDestroy()가 호출되며 시스템은 새 액티비티 인스턴스를 만들어 해당 인스턴스에 대해 onCreate()를 호출.

사용자가 액티비티를 떠나기 시작하면 시스템에서 메서드를 호출하여 액티비티를 중단.
사용자가 다른 앱으로 전환하는 경우처럼 부분적으로만 중단되고 메모리에 남아있는 경우가 있을 수 있으며 액티비티가 포그라운드로 돌아올 수 있음.

사용자가 액티비티로 돌아오면 중단된 지점부터 다시 시작됨.



액티비티 상태와 메모리

시스템이 RAM을 확보해야 할 때 프로세스를 종료함. 특정 프로세스를 종료할 가능성은 프로세스의 상태에 따라 달라짐.

시스템이 액티비티를 직접 종료하지 않으며, 액티비티가 실행되는 프로세스를 종료하여 액티비티뿐만 아니라 프로세스에서 실행 중인 다른 모든 요소들도 함께 파괴함.

아래의 표는 프로세스가 다른 유형의 앱 컴포넌트를 실행하고 있지 않은 경우에 적용됨.

종료 가능성프로세스 상태최종 액티비티 상태
가장 낮음포그라운드 (포커스가 있거나 곧 포커스를 받을 예정)Resumed
낮음화면에 보임(포커스 없음)Started/Paused
높음백그라운드 (보이지 않음)Stopped
가장 높음비어있음Destroyed



UI 상태 저장 및 복원

화면 회전, 멀티 윈도우 모드 전환같은 구성 변경 시 시스템에서는 액티비티를 파괴하여 액티비티 인스턴스에 저장된 UI 상태를 초기화 시킴.

다른 앱으로 전환했다가 다시 앱에 돌아올 때, 프로세스가 파괴되는 경우에도 동일한 UI 상태를 유지하지 못함.

ViewModel, onSaveInstanceState() 를 사용하여 UI 상태 보존하는 것 가능.


시스템에서 이전 상태를 복원하는 데 사용하는 데이터 -> instance state
instance state는 Bundle 객체에 저장된 key-value 쌍의 모음임.

※ 앱의 view 상태를 복원하기 위해서는 android:id 속성으로 제공된 고유 ID가 있어야 함.

onSaveInstanceState() 사용

액티비티가 중지되면 시스템은 onSaveInstance() 메서드를 호출하여 액티비티 상태 정보를 Bundle에 저장할 수 있도록 함.

View에 대한 일시정인 정보(ex: EditText의 텍스트, ListView의 스크롤 위치)를 저장함.

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state.
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

사용자가 직접 액티비티를 닫을 때나 finish()가 호출되는 다른 경우 onSaveInstanceState()가 호출되지 않음.


UI 상태 복원

액티비티가 소멸 후 다시 생성되는 경우 시스템이 액티비티에 전달하는 Bundle을 통해 저장된 인스턴스 상태를 복구할 수 있음.
onCreate()onRestoreInstanceState() 콜백 메서드 모두 인스턴스 상태가 포함된 동일한 Bundle을 수신.

1. onCreate()에서 복원

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state.
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

2. onRestoreInstanceState()에서 복원

onStart() 이후에 호출되는 onRestoreInstanceState()을 구현하여 복원할 수 있음.
이 경우에 시스템은 복원할 저장된 액티비티가 있을 경우에만 onRestoreInstanceState()을 호출하므로 Bundle이 null인지 체크하지 않아도 됨.

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState) //수퍼클래스를 반드시 호출

    // Restore state members from saved instance.
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

액티비티 간 이동

액티비티에서 다른 액티비티 시작

startActivity() 혹은 startActivityForResult() 메서드를 통하여 새로운 액티비티를 시작. 두 가지 경우 모두 Intent 객체를 전달.

startActivity()

새로 시작된 액티비티가 결과를 리턴할 필요가 없는 경우 사용.

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

startActivityForResult()

액티비티가 종료될 경우 결과값을 가져와야 하는 경우에 사용.
예를 들어 연락처 목록에서 연락처를 선택할 수 있는 액티비티를 시작할 경우 액티비티가 종료되면 선택한 연락처를 리턴함.

결과값은 onActivityResult()를 통해 반환됨.

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    // A contact was picked. Display it to the user.
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

A액티비티에서 B액티비티를 시작할 경우 B액티비티가 만들어지기 전에 A액티비티가 완전히 중지되는 것이 아님. B액티비티를 시작하는 프로세스와 A액티비티를 중지하는 프로세스가 겹침.

  1. A 액티비티의 onPause() 메서드가 실행됨.
  2. B 액티비티의 onCreate(), onStart(), onResume()이 순서대로 실행됨. 사용자 포커스가 B에게 있음.
  3. A 액티비티가 화면에 표시되지 않으며 A의 onStop()이 실행됨

수명 주기 콜백을 이용해 한 액티비티에서 다른 액티비티로의 정보 전환 관리 가능.

0개의 댓글