본문 바로가기
2023년 이전/Android

IllegalArgumentException :cannot add the same observer with different lifecycles & viewLifecycleOwner

by JeongUPark 2021. 12. 5.
반응형

참고

https://uchun.dev/caution-when-using-a-fragment-viewLifecycleOwner/

 

Fragment 에서 ViewLifecycleOwner 사용 시 주의점

시작하기에 앞서

uchun.dev

Android 개발을 하다가 다음과 같은 exception Log를 보게 되었습니다.

java.lang.IllegalArgumentException: Cannot add the same observer with different lifecycles

발생한 이유는 LiveData의 Observer가 다른 owner에 포함이 되어 있다면 , LiveData는 위의 exception을 반환합니다.

즉, 하나의 activity에 있는 viewModel의 LiveData를 가져다 사용하는데, 아래와 같이 작성 하였더니

LiveData.observe(this, Observer{}) 

위의 IllegalArgumentException이 발생하였다. 이유는 activity에 포함된 viewmodel이고, 거기에 맞는 Lifecycle을 써야하는데 fragment lifecycle을 사용하여 발생한 문제 인것 같습니다.

그래서 this대신 다음과 같이 하면 좋습니다.

LiveData.observe(viewLifecycleOwner, Observer{})

로 하면 정상으로 동작하였습니다.

(일반적인 사용방법은 문제 없는데 Fragment에서 activity에 있는 Viewmodel을 끌어다 쓸 경우 이런 문제가 발생하는것 같습니다.

그리고 공부하면서 위의 viewLifecyclerOwner의 상세 코드를 보면서 viewLifecyclerOwner에 대해서 쪼금 공부를 해보았습니다.

@MainThread
@NonNull
public LifecycleOwner getViewLifecycleOwner() {
    if (mViewLifecycleOwner == null) {
        throw new IllegalStateException("Can't access the Fragment View's LifecycleOwner when "
                + "getView() is null i.e., before onCreateView() or after onDestroyView()");
    }
    return mViewLifecycleOwner;
}

즉, LifecycleOwener 도 exception이 발생할 수 있습니다. 이 mViewLifecycleOwner은 언제 null 처리되고 언제 생성이 될까요?

아래 코드를 보면

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);
        if (mView != null) {
            // Initialize the view lifecycle
            mViewLifecycleOwner.initialize();
            // Tell the fragment's new view about it before we tell anyone listening
            // to mViewLifecycleOwnerLiveData and before onViewCreated, so that calls to
            // ViewTree get() methods return something meaningful
            ViewTreeLifecycleOwner.set(mView, mViewLifecycleOwner);
            ViewTreeViewModelStoreOwner.set(mView, mViewLifecycleOwner);
            ViewTreeSavedStateRegistryOwner.set(mView, mViewLifecycleOwner);
            // Then inform any Observers of the new LifecycleOwner
            mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
        } else {
            if (mViewLifecycleOwner.isInitialized()) {
                throw new IllegalStateException("Called getViewLifecycleOwner() but "
                        + "onCreateView() returned null");
            }
            mViewLifecycleOwner = null;
        }
    }

onCreateView 호출 이전에 FragmentViewLifecycleOwner 가 생성됩니다.

그러므로 onCreateView 에서 ViewLifecycleOwner 를 사용해도 문제가 없습니다.

그리고 null은 Fragment가 생성되지 않거나 아래 코드 처럼 Lifecycle.Event.ON_DESTROY가 발생하면 발생되는 것으로 보입니다. (명확하지는 않습니다. 열심히 뒤져봤는데 null 처리는 위의 performCreateView에서 밖에 찾지 못하였습니다.)

void performDestroyView() {
        mChildFragmentManager.dispatchDestroyView();
        if (mView != null && mViewLifecycleOwner.getLifecycle().getCurrentState()
                        .isAtLeast(Lifecycle.State.CREATED)) {
            mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        }
        mState = CREATED;
        mCalled = false;
        onDestroyView();
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onDestroyView()");
        }
        // Handles the detach/reattach case where the view hierarchy
        // is destroyed and recreated and an additional call to
        // onLoadFinished may be needed to ensure the new view
        // hierarchy is populated from data from the Loaders
        LoaderManager.getInstance(this).markForRedelivery();
        mPerformedCreateView = false;
    }

아무튼 onCreate전에 생성되기 때문에 언제든 써도 상관이 없는 것으로 보입니다.

반응형