
The activity lifecycle | Android Developers

class MainActivity : AppCompatActivity() {
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// 리셋되는 카운터
var counter = 100
binding.textView.text = counter.toString()
binding.button.setOnClickListener {
counter += 1
binding.textView.text = counter.toString()
}
}
}
var counter = 100 이 수행되어 값이 초기화됨onSaveInstanceState를 만들어 제공함
ref) 표 출처Map형식으로 Bundle에 데이터를 저장하고 onCreate에서 null체크를 한 뒤 값을 가져오면 된다.Bundle이 거대한 Data를 다루기 위해 만들어진 포맷이 아님
ViewModel overview | Android Developers
Activity와는 독립된 생명주기를 가짐
→ Actvitiy가 파괴되고 재생성되는 동안에도 ViewModel은 살아있음

class MainActivity : AppCompatActivity() {
private val binding:ActivityMainBinding by lazy{
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
val myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
//viewModel 인스턴스를 그냥 생성하면 경우에 따라서는 인스턴스가 여러 개 만들어지는 문제가 발생하기 떄문에
// ViewModelProvider을 통해서 인스턴스를 싱글톤으로 생성한다.
myViewModel.counter = 100
binding.textView.text = myViewModel.counter.toString()
binding.button.setOnClickListener {
myViewModel.counter += 1
binding.textView.text = myViewModel.counter.toString()
}
}
}
class MyViewModel(): ViewModel() {
var counter:Int = 0
}
[문제]
위 코드는 ViewModel을 사용하고 있지만 onCreate에서 counter값을 100으로 지정하는 로직이 존재해 Activity가 재생성될 때마다 counter값이 100으로 돌아가게 됨.
[해결]
ViewModel을 초기화할 때 counter의 초기값인 100을 건네주고 나머지 로직에서는 저장된 값을 사용하도록 함
ViewModelProvider로 ViewModel 객체를 만들 때 초기값을 전달하는 것이 금지되어 있으므로 Factory 패턴을 사용해야 한다.
→ ViewModelProviderFactory를 상속받는 Factory 클래스를 만들어준다.
ViewModelProvider.Factory를 상속받는 Factory 클래스
@Suppress("UNCHECKED_CAST")
class MyViewModelFactory(private val counter:Int): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if(modelClass.isAssignableFrom(MyViewModel::class.java)) {
return MyViewModel(counter) as T
}
throw IllegalArgumentException("Viewmodel class not found")
}
}
class MainActivity : AppCompatActivity() {
private val binding:ActivityMainBinding by lazy{
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//factory
val factory = MyViewModelFactory(100)
//ViewModel에 전달하고 싶은 초기값을 Factory에 전달한다.
val myViewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)
//ViewModelProvider는 Factory를 같이 받는다.
//=> ViewModelProvider가 Factory를 통해서 MyViewModel을 생성할 수 있도록 한다.
binding.textView.text = myViewModel.counter.toString()
binding.button.setOnClickListener {
myViewModel.counter += 1
binding.textView.text = myViewModel.counter.toString()
}
}
}
class MyViewModel(_counter : Int): ViewModel() {
var counter = _counter
}
Activity가 재생성되어도 ViewModel에 있는 값을 가져오기 때문에 유지가 된다.
Activity가 아닌 ViewModel이 데이터를 가지게 하면 Lifecycle이 분리되기 때문에 화면이 회전해도 상태를 신경쓸 필요가 없다. 다만 앱이 강제 종료될 경우에는 ViewModel도 데이터를 유지할 수 없다.
→ 앱이 종료되어도 viewModel의 값을 저장하고 싶다면 SavedStateHandle 을 사용하면 된다.
ViewModel의 저장된 상태 모듈 | Android 개발자 | Android Developers
@Suppress("UNCHECKED_CAST")
class MyViewModelFactory(
private val counter: Int,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null,
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {package com.getupmina.android.viewmodellifecycle
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.ViewModelProvider
import com.getupmina.android.viewmodellifecycle.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private val binding:ActivityMainBinding by lazy{
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// var counter = 100
// binding.textView.text = counter.toString()
//
// binding.button.setOnClickListener{
// counter += 1
// binding.textView.text = counter.toString()
// }
/* val myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
myViewModel.counter = 100
binding.textView.text = myViewModel.counter.toString()
binding.button.setOnClickListener {
myViewModel.counter += 1
binding.textView.text = myViewModel.counter.toString()
}
*/
//factory
val factory = MyViewModelFactory(100,this)
val myViewModel by viewModels<MyViewModel>(){factory}
binding.textView.text = myViewModel.counter.toString()
binding.button.setOnClickListener {
myViewModel.counter += 1
binding.textView.text = myViewModel.counter.toString()
myViewModel.saveState() //counter값을 늘릴 때마다 저장
}
}
}
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle,
): T {
if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
return MyViewModel(counter, handle) as T
//viewModel을 반환할 때 handle울 같이 반환하도록 함.
}
throw IllegalArgumentException("Viewmodel class not found")
}
}
class MainActivity : AppCompatActivity() {
private val binding:ActivityMainBinding by lazy{
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//factory
val factory = MyViewModelFactory(100,this)
val myViewModel by viewModels<MyViewModel>(){factory}
binding.textView.text = myViewModel.counter.toString()
binding.button.setOnClickListener {
myViewModel.counter += 1
binding.textView.text = myViewModel.counter.toString()
myViewModel.saveState() //counter값을 늘릴 때마다 저장
}
}
}
class MyViewModel(_counter: Int, private val savedStateHandle: SavedStateHandle) : ViewModel() {
var counter:Int = savedStateHandle.get<Int>(SAVE_STATE_KEY)?:_counter
//null이면 전달받은 초기값을 사용할 수 있도록 설정
fun saveState(){
savedStateHandle.set(SAVE_STATE_KEY,counter)
}
companion object{
private const val SAVE_STATE_KEY = "counter"
} //key를 설정
}
냉동코더의 알기 쉬운 Modern Android Development 입문 강의 - 인프런
출처 : 냉동코더의 알기 쉬운 Modern Android Development 입문
https://github.com/cliearl/book-search-app
Designed and developed by 2022 FrozenCoder
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.