티스토리 뷰

private val mainViewModel by lazy { ViewModelProvider(requireActivity()).get(MainViewModel::class.java) }

기본적으로 ViewModel을 생성할 때 ViewModelProvider를 생성하고 get()함수를 통해 ViewModel을 얻습니다. (ViewModel을 직접 생성하는 방법은 잘못된 사용법입니다.)

 

public open class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
)

public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
    owner.viewModelStore,
    factory
)

ViewModelProvider의 생성자를 확인하면 ViewModelStoreOwnerViewModelFactory를 받습니다. owner를 통해 ViewModelStore를 얻습니다.

 

@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStoreOwner를 보면 getViewModelStore()를 통해 ViewModelStore를 얻을 수 있으며, ViewModelStore는 내부적으로 HashMap을 사용하여 ViewModel을 관리합니다.


public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {
        
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
        ActivityResultCaller {

ComponentActivity(AppCompatActivity -> FragmentActivity -> CompoentActivity)는 ViewModelStoreOwner를 상속받고 있습니다.

 

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

ActivitygetViewModelStore를 살펴보면 ensureViewModelStore()에서 getLastNonConfigurationInstance()를 통해 마지막으로 저장된 NonConfigurationInstance가 있는지 확인하고 없으면 ViewModelStore을 새롭게 만듭니다.


getLastNonConfigurationInstance

@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

final void performResume(boolean followedByPause, String reason) {
...
    mLastNonConfigurationInstances = null;
...
}

getLastNonConfigurationInstance()는 이전 Activity에서 onRetainNonConigurationInstance를 호출하여 반환된 NonConfigurationInstances를 onCreate ~ onStart 사이에 호출하여 받아올 수 있습니다.

Activity를 확인하면 performResume()이 호출되면 mLastConfigurationInstancesnull이 저장되기 때문에 onCreate ~ onStart 사이에서 호출할 수 있는 것으로 보입니다.

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(Context context, ActivityThread aThread,
    Instrumentation instr, IBinder token, int ident,
    Application application, Intent intent, ActivityInfo info,
    CharSequence title, Activity parent, String id,
    NonConfigurationInstances lastNonConfigurationInstances,
    Configuration config, String referrer, IVoiceInteractor voiceInteractor,
    Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
    IBinder shareableActivityToken) {

    ...

    mLastNonConfigurationInstances = lastNonConfigurationInstances;

    ...
    
}

mLastNonConfigurationInstances가 초기화 되는 곳은 attach입니다. attach를 통해 ActivityWindow에 붙을 때 초기화되고 performResume이 호출되면서 사라지는 것 같습니다.


@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}
static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

onRetainNonConfigurationInstanceNonConfigurationInstances를 보면 onRetainNonConfigurationInstance에서 ViewModelStoreNonConfigurationInstances를 통해 저장해서 반환합니다.

 

onRetainNonConfigurationInstance

Android Developer에서 onRetainNonConfiguration을 보면 시스템의 의해 onStoponDestroy 사이에서 호출되며 주 목적은 Configuration Change가 발생할 때 데이터를 보존하기 위함이고 string, layout 등 Configuration Change가 발생하여 변할 수 있는 값을 반환하면 안된다.


 

마무리

1.

구성 요소 변경이 발생할 때 시스템이 onRetainNonConfigurationInstance를 호출하며 NonConfigurationInstancesViewModelStore를 저장하여 반환하고 재생성된 Activityattach될 때 onRetainNonConfigurationInstance로 반환한 NonConfigurationInstances 불러옵니다.

 

2.

ViewModelStore를 호출할 때 getLastNonConfigurationInstnace를 호출하여 NonConfigurationInstances에서 ViewModelStore를 불러오며 ViewModelStore를 유지할 수 있고 덕분에 ViewModel을 유지할 수 있습니다.

 

performResume가 수행되면 NonConfigurationInstaces를 잃기 때문에 onCreate ~ onStart 사이에서 ViewModel을 초기화 하는 것이 옳바른 것 같습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함