Android

Fragment LiveData 와 viewLifecycleOwner

반응형

문제

  1. viewModel에서 LiveData를 선언하고
  2. Fragment에서 observe를 할 때 LifecycleOwner 인자에 this(fragment)를 넘긴다
  3. A fragment에서 B fragment로 replace한다(addToBackStack 설정 - 폐기되지 않고 뒤로가기 누르면 A로 돌아감)
  4. 뒤로가기를 통해 A fragment로 돌아가서 observe하고 있는 변수에 대해 이벤트를 발생시키면 2번 trigger 된다


https://developer.android.com/guide/fragments/lifecycle

 

Fragment lifecycle  |  Android Developers

Fragment lifecycle Each Fragment instance has its own lifecycle. When a user navigates and interacts with your app, your fragments transition through various states in their lifecycle as they are added, removed, and enter or exit the screen. To manage life

developer.android.com

 

Activity와 Fragment의 생명주기

 

Fragment가 더 복잡하다

출처 : The Android Lifecycle cheat sheet — part III : Fragments

Fragment는 하나의 Activity안에서 여러개의 Fragment를 동작할 수 있으며 Activity의 생명주기를 추종하며 보다 복잡한 생명주기를 갖습니다.

Fragment의 생명주기는 Attach 되는 Activity의 생명주기에 영향을 받습니다. 예를 들어, Activity가 일시 정지되면 그 안에 모든 Fragment도 일시 정지되며 Activity가 파괴될 때 모드 Fragment도 마찬가지로 파괴됩니다. Activity가 실행 중인 동안에는 Fragment를 추가 또는 제거를 할 수 있습니다. 이처럼 Fragment는 Fragment 자체의 생명주기를 갖기도 합니다.

Fragment에서의 LiveData 사용

class MainViewModel : ViewModel() {
    private val _isUpdate = MutableLiveData<Boolean>(false)
    val isUpdate: LiveData<Boolean>
        get() = _isUpdate
    // _isUpdate를 Private로 선언 후 
    // isUpdate를 따로 선언해주는 이유는 LiveData를 내부에서만 접근할 수 있도록 하기위함
    // viewModel 클래스에서만 _isUpdate에 접근 가능하다

    var test = MutableLiveData<String>("test")

    fun setIsUpdate(b: Boolean) {
        _isUpdate.postValue(b)
    }

}
class FirstFragment : Fragment() {
    private val viewModel: MainViewModel by viewModels()
	
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.isUpdate.observe(this) {
            Log.d("로그", "[onChanged]: " + hashCode());

        }
}

간단한 viewModel과 Fragment 코드이며 Fragment에서 this를 넘기며 observe하고 있습니다.

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
        ActivityResultCaller {
        ....

LiveData를 observe시 사용되는 첫번째 파라미터는 LifecycleOwner 인터페이스이며, Fragment는 LifecycleOwner를 구현하고 있으므로 this로 넘길 수 있습니다.

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

...

 

Observe 두번 trigger

1&nbsp; &nbsp;Fragment -&gt; 2 Fragment -&gt; 뒤로가기(1 Fragment) -&gt; 버튼클릭으로 observe 이벤트

binding.btnFragment1.setOnClickListener {
    supportFragmentManager.beginTransaction().apply {
        replace(R.id.flFragment, firstFragment)
        addToBackStack(null)
        commit()
    }
}

binding.btnFragment2.setOnClickListener {
    supportFragmentManager.beginTransaction().apply {
        replace(R.id.flFragment, secondFragment)
        addToBackStack(null)
        commit()
    }
}


위 화면 1 Fragment -> 2 Fragment -> 뒤로가기 -> 1 Fragment -> 버튼클릭(setIsUpdate() 호출)
옵저빙을 중복으로 하고 있음을 알 수 있다.

문제가 되는 곳은 바로

viewModel.isUpdate.observe(this) {

위 코드는 LiveData observe의 LifecycleOwner에 해당하는 파라미터로 Fragment 자신을 넘기는 코드입니다. 다음으로 또 다른 Activity와 Fragment의 Lifecycle의 흐름을 살펴보겠습니다.

출처 : The Android Lifecycle cheat sheet — part III : Fragments

Activity or Fragment가 Resumed 되었을 때 흐름입니다. Fragment가 Destroyed 되지 않고 onCreateView가 여러 번 호출 될 수 있음을 확인할 수 있습니다.

Fragment 의 Lifecycle과 Fragment View Lifecycle

 


Fragment Lifecycle <-> Fragment View Lifecycle은 다르다.

  • Fragment Lifecycle : Create ~ Destroy
  • Fragment View Lifecycle : CreateView ~ DestroyView

주의깊게 봐야할 것은 View Lifecycle은 onDestroyView()에서 DESTROYED 된다는 점

Fragment에서의 LiveData 올바르게 사용하기

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    viewModel.isUpdate.observe(viewLifecycleOwner) {
        Log.d("로그", "[onChanged]: " + hashCode());
	// TODO
    }

위와 같이 사용한다면 replace될 때 onDestroyView에서 View Lifecycle은 폐기되기 때문에 중복으로 Observe하는 가능성을 배제시켜 준다.

꼭 onViewCreated에서 Observe 해야 하나요?

사실 onCreateView에서 사용해도 문제가 없다고 생각합니다.

void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
    mChildFragmentManager.noteStateNotSaved();
    mPerformedCreateView = true;
    mViewLifecycleOwner = new FragmentViewLifecycleOwner(this, getViewModelStore());
    mView = onCreateView(inflater, container, savedInstanceState);
    ...

위처럼 onCreateView가 불리기 전에 viewLifecycleOwner가 생성되기에 onCreateView에서 viewLifecycleOwner를 사용할 수 있습니다. 하지만 onCreate에서는.. 안되겠죠?

결론

 

  • Fragment에서 LiveData를 사용할 때 this 대신 viewLifecycleOwner를 사용하자
  • onCreate() 대신 onCreateView or onViewCreated에서 observe 하자



참고

https://uchun.dev/caution-when-using-a-fragment-viewLifecycleOwner/https://pluu.github.io/blog/android/2020/01/25/android-fragment-lifecycle/

반응형