본문 바로가기

코딩 이야기/안드로이드&코틀린 공부

[안드로이드&코틀린 공부] Activity, Fragment, AAC ViewModel, Lifecycle

반응형

Activity

안드로이드 앱은 핵심적으로 액티비티, 서비스, 브로드캐스트 수신자, 내용 제공자 이 4가지로 이루어져 있습니다.

액티비티는 안드로이드의 핵심이 되는 4대 컴포넌트 중 하나입니다.

액티비티는 사용자에게 UI를 제공하여 어플리케이션과 상호작용하는 단일 화면을 의미합니다.

Oncreate 함수는 액티비티를 생성할 때 필수적으로 구현해야합니다. 이 함수의 범위 안에서 setContentView를 이용해서 레이아웃 리소스를 정의하고, findViewById를 사용하여 위젯들과 상호작용하도록 할 수 있습니다.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
} //activity_main.xml 파일과 클래스를 합니다.

또한 액티비티를 사용하기 위해서는 AndroidManifest.xml에 해당 액티비티가 선언이 되어있어야합니다.

<manifest ... >
    <application ... >
        <activity android:name="com.example.myapp.MainActivity" ... >
        </activity>
    </application>
</manifest>

액티비티는 생명주기 동안 여러 상태를 거칩니다.

생명주기에 대해 간단하게 설명하자면 생명주기(Lifecycle)는 프로세스가 생겨나고 다시 파괴되기까지의 과정이라 보면 편합니다. 액티비티, 프래그먼트, 서비스 이 3개가 수명주기를 가집니다.

지금은 액티비티의 수명주기에 대해서 알아보겠습니다.

onCreate()

시스템이 활동을 생성할 때 실행되는 이 콜백을 사용해야 합니다. 구현 시 활동의 필수 구성요소를 초기화해야 합니다. 예를 들어 앱은 여기에서 뷰를 생성하고 데이터를 목록에 결합해야 합니다. 이 콜백에서 setContentView 호출하여 활동의 사용자 인터페이스를 위한 레이아웃을 정의할 수 있습니다.

onStart()

onCreate() 종료 후 액티비티가 화면에 표시되기 직전에 호출됩니다.

화면에 진입할 때마다 실행되어야하는 작업이 여기서 이뤄집니다.

onResume()

액티비티가 사용자에게 보여진 직후, 사용자와 상호작용하기 직전에 호출됩니다. 액티비티가 사용자에게 포커스인되어있을 때이기 때문에 해당 액티비티가 액티비티 스택 최 상단에 위치해있으며, 앱의 핵심 기능이 이 onResume()에서 구현된다고 합니다.

이 부분부터 사용자와 UI 상호작용

onPause()

이름 그대로 액티비티가 포커스를 잃고 일시정지할 때 onPause()가 호출됩니다. 즉 액티비티가 화면에 보여지지 않게 된 직후에 호출됩니다. 사용자가 뒤ㅎ로가기 버튼를 누르는 상황이 예시로 들 수 있습니다.

엄밀히 말하면 사용자가 액티비티를 떠난 것이므로 다음 콜백으로 onStop() 혹은 onResume()이 올 수 있습니다.

onStop()

액티비티가 사용자에게 더 이상 표시되지 않을때, 시스템은 onStop()을 호출합니다. 해당 액티비티가 제거중이거나, 새 액티비티가 시작중이거나 중지된 액티비티를 다루고 있기 때문에 발생할 수 있습니다. 이후에 onRestart()로 이어지거나 onDestroy()로 이어질 수 있습니다.

onRestart()

중지된 상태이던 액티비티가 다시 시작할 때 이 콜백을 호출합니다.

onDestroy()

onDestroy()는 액티비티가 호출할 수 있는 마지막 콜백입니다. 액티비티나 액티비티가 포함된 프로세스가 제거될 때 액티비티의 모든 리소스를 해제하도록 구현합니다.

Fragment

Fragment 는 코드를 더 모듈화하도록 돕고, 큰 화면을 정교하게 Ui를 구성하고, 큰 화면과 작은 화면 간의 비율을 돕습니다.

프래그먼트는 액티비티의 모듈형 섹션이라 봐도 무방합니다. 한 액티비티 내에서 여러가지의 프래그먼트를 사용할 수 있으며, 반대로 한 프래그먼트를 여러 액티비티에서 사용할 수 있습니다.

 

프래그먼트는 항상 액티비티에 호출되어야합니다.

해당 액티비티와 프래그먼트의 수명주기는 액티비티가 일시정지 되는 경우, 프래그먼트도 같이 일시정지가 되며, 액티비티가 소멸될 경우, 프래그먼트도 같이 소멸됩니다.

그러나 액티비티가 실행 중인 동안에는 각 프래그먼트를 추가 또는 제거하는 등 개별적으로 조작할 수 있습니다.

이 점을 이용하여 액티비티의 레이아웃을 여러 프래그먼트로 나눠서 런타임 중에 액티비티의 외관을 수정할 수도 있고, 그러한 변경한 내용을 액티비티가 관리하는 백 스택에서 보존할 수도 있습니다.

프래그먼트를 액티비티 레이아웃에 추가하면, 해당 프래그먼트는 액티비티의 뷰 계층 내에서 ViewGroup에 들어가고 자체적인 뷰 레이아웃을 정의한다고 합니다.

직접사용해보면 xml 파일안에 뷰 사용하듯이 사용하게 됩니다.

프래그먼트의 수명주기

프래그먼트의 수명주기 콜백 또한 액티비티와 매우 유사합니다.

예를 들어 액티비티가 onPause()를 받으면 프래그먼트도 onPause()를 받습니다.

하지만 프래그먼트가 프래그먼트의 UI를 빌드하고 소멸시키는 등의 같은 작업을 수행하기 위해 액티비티와 고유한 상호작용을 처리하는 추가적인 콜백이 몇 개 더 존재합니다.

onAttach()

프래그먼트가 액티비티와 연결되어 있었던 경우 호출됩니다

이부분에서 액티비티가 전달된다고 합니다.

onCreateView()

프래그먼트와 연결된 뷰 계층을 생성하기 위해 호출됩니다.

onActivityCreated()

이름 그대로 액티비티의 onCreated()가 반환될 때 호출됩니다.

onDestroyView()

프래그먼트와 연결된 뷰 계층이 제거되는 중일 때 호출됩니다.

onDetach()

액티비티와 연결이 끊어지는 중일때 호출됩니다.

AAC ViewModel

ViewModel이란 안드로이드 jetpack 구성 요소 중 하나로 MVVM(model-view-viewModel) 디자인 패턴으로부터 파생되었다고 합니다.

그래서 MVVM의 뷰모델과 Android Jetpack의 뷰모델을 구분하기 위해서 Android Jetpack의 뷰모델을 Android Architecture Viewmodel 의 약자인 AAC ViewModel이라고 부른다고 합니다.

필요성

중요한 데이터들을 따로 ViewModel에서 관리하기 때문에 Activity나 Fragment에 과도한 책임이 생겨 클래스가 커지는 것을 방지하고 유지보수, 재사용성, 테스트성을 높이는데 기여합니다.

수명주기를 파악하여 UI와 관련된 데이터를 저장하고 관리하기 위해 만들어졌습니다. 이로 인해 configuration change에서 데이터를 유지할 수 있도록 합니다.

configuration change의 예시로는 다크모드나 화면 회전 등이 있습니다.

이러한 점을 해결하기 위해 onSaveInstanceState()를 사용하여 데이터를 유지할 수 있다곤 하나 비트맵이나 유저 리스트 같은 큰 데이터의 경우 적합하지 않습니다.

의도

액티비티나 프래그먼트는 정말 UI controller 로서 view를 보여주거나 사용자의 input을 받는 등 UI와 관련된 로직만을 가지고 있어야합니다. 데이터 처리나 이러한 부분들은 viewmodel과 나머지에서 하도록 분리시켜야합니다.

사용법

AAC ViewModel은 추상 클래스로서 상속을 통해서 사용해야합니다.

class SimpleViewModel : ViewModel() {
    override fun onCleared() {
        super.onCleared()

    }
    
}

ViewModel 인스턴스는 ViewModelProvider를 이용하여 생성합니다.

이부분에서 this를 넘겨주게 되는데 이는 이 뷰모델을 메인액티비티에서 사용함을 의미합니다.

ViewModelStore를 누가 소유하고 있느냐인데, 여기선 메인 액티비티가 소유하게 됩니다.

ViewModelStore은 뷰모델 객체가 해시맵 구조로 저장되는 곳이라고 합니다.

class MainActivity : AppCompatActivity() {
 
    private lateinit var binding: ActivityMainBinding
    private lateinit var simplViewModel: SimpleViewModel
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        simpleViewModel = ViewModelProvider(this).get(SimpleViewModel::class.java)
        binding.user = userViewModel
 
        simpleViewModel.height.observe(this, Observer {
            binding.textViewHeight.text = it.toString()
        })
    }
}

주의점

  • MVVM의 ViewModel : View에 필요한 데이터를 관리하여 바인딩 해주고, 비즈니스 로직을 담당해 처리하는 요소
  • AAC의 ViewModel : Android의 수명주기를 고려하여 UI 관련 데이터를 저장하고 관리하는 요소

로 둘다 ViewModel이지만 개념자체는 차이가 있습니다.

Lifecycle

위에서 간략하게 다뤘던 수명주기, 라이프사이클에 대해서 좀 더 다뤄보겠습니다.

사용자가 앱을 탐색하거나, 나가거나, 다시 돌아오는 등의 행위를 할 경우 액티비티는 수명 주기 단계 안에서 여러 단계로 전환됩니다. 액티비티 클래스는 이 변화를 알아차릴 수 있는 콜백을 제공하고 이는 위에 액티비티에서 다뤘습니다.

수명주기의 패러다임과 내부에서 어떤 일이 일어나는 지 더 자세히 다뤄보겠습니다.

기본 개념

사용자가 액티비티를 벗어나면 시스템을 액티비티를 해제하기 위한 메서드를 호출합니다. 경우에 따라 액티비티 부분적으로만 해체되며 여전히 메모리에 남아 있습니다. 예를 들어 사용자가 다른 앱으로 전환하는 경우입니다. 이러한 경우에는 액티비티가 다시 포그라운드로 돌아올 수 있습니다.

사용자가 해당 액티비티로 다시 돌아가면 정지했던 지점부터 액티비티가 재개됩니다.

액티비티의 정도에 따라서 콜백 메소드를 자유롭게 구현할 수 있습니다.

액티비티 상태와 메모리

시스템은 메모리의 여유공간을 위해서 프로세스를 종료하고 프로세스의 상태에 따라서 종료할 가능성이 달라집니다. 이 밑의 표는 안드로이드 공식 사이트에서 가져왔습니다.

 

시스템은 메모리 공간을 확보하기 위해 절대 액티비티를 직접 종료하지 않습니다. 그 대신, 액티비티를 실행하는 프로세스를 종료하여 액티비티뿐만 아니라 프로세스에서 실행되는 다른 모든 작업을 함께 소멸시킵니다.

하지만 이로 인해서 필요로 했던 데이터가 소멸되거나, UI가 변경되는 등의 문제가 발생할 수 있습니다.

이를 막기 위해서 위에서 설명한 ViewModel과 onSaveInstanceState()를 사용하게 됩니다.

위에서 말했듯 데이터가 커질 수록 ViewModel을 사용하고 가벼울 수록 onSaveInstanceState() 만으로 해결된다고 했습니다.

그럼에도 안드로이드 공식 사이트는 직렬화/역직렬화 비용을 위해서 두 방식 둘다 사용해야한다고 합니다.

인스턴스 상태

사용자가 의도해서 액티비티를 종료시키는 경우, 뒤로가기를 누르거나 혹은 finish()를 통해 정상적으로 종료될 경우 액티비티는 말끔하게 사라집니다.

하지만 시스템이 메모리 부족이나 Configure Change로 인해 소멸하려는 경우 액티비티가 똑같이 소멸되긴 하지만 존재했다는 정보가 남게 되어, 사용자가 해당 액티비티로 다시 돌아가려하는 경우 이 데이터 세트를 이용하여 새로운 액티비티의 인스턴스를 만든다고 합니다.

 

반응형