Manually clearing an Android ViewModel?

后端 未结 12 1004
独厮守ぢ
独厮守ぢ 2020-12-04 18:50

Edit: This question is a bit out of date now that Google has given us the ability to scope ViewModel to navigation graphs. The better approach (rather

相关标签:
12条回答
  • 2020-12-04 19:16

    If you don't want the ViewModel to be scoped to the Activity lifecycle, you can scope it to the parent fragment's lifecycle. So if you want to share an instance of the ViewModel with multiple fragments in a screen, you can layout the fragments such that they all share a common parent fragment. That way when you instantiate the ViewModel you can just do this:

    CommonViewModel viewModel = ViewModelProviders.of(getParentFragment()).class(CommonViewModel.class);
    

    Hopefully this helps!

    0 讨论(0)
  • 2020-12-04 19:18

    As I know you can't remove ViewModel object manually by program, but you can clear data that stored in that,for this case you should call Oncleared() method manually for doing this:

    1. Override Oncleared() method in that class that is extended from ViewModel class
    2. In this method you can clean data by making null the field that you store data in it
    3. Call this method when you want clear data completely.
    0 讨论(0)
  • 2020-12-04 19:24

    In my case, most of the things I observe are related to the Views, so I don't need to clear it in case the View gets destroyed (but not the Fragment).

    In the case I need things like a LiveData that takes me to another Fragment (or that does the thing only once), I create a "consuming observer".

    It can be done by extending MutableLiveData<T>:

    fun <T> MutableLiveData<T>.observeConsuming(viewLifecycleOwner: LifecycleOwner, function: (T) -> Unit) {
        observe(viewLifecycleOwner, Observer<T> {
            function(it ?: return@Observer)
            value = null
        })
    }
    

    and as soon as it's observed, it will clear from the LiveData.

    Now you can call it like:

    viewModel.navigation.observeConsuming(viewLifecycleOwner) { 
        startActivity(Intent(this, LoginActivity::class.java))
    }
    
    0 讨论(0)
  • 2020-12-04 19:29

    I found a simple and fairly elegant way to deal with this issue. The trick is to use a DummyViewModel and model key.

    The code works because AndroidX checks the class type of the model on get(). If it doesn't match it creates a new ViewModel using the current ViewModelProvider.Factory.

    public class MyActivity extends AppCompatActivity {
        private static final String KEY_MY_MODEL = "model";
    
        void clearMyViewModel() {
            new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).
                .get(KEY_MY_MODEL, DummyViewModel.class);
        }
    
        MyViewModel getMyViewModel() {
            return new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()).
                .get(KEY_MY_MODEL, MyViewModel.class);
        }
    
        static class DummyViewModel extends ViewModel {
            //Intentionally blank
        }
    }   
    
    0 讨论(0)
  • 2020-12-04 19:31

    Quick solution without having to use Navigation Component library:

    getActivity().getViewModelStore().clear();
    

    This will solve this problem without incorporating the Navigation Component library. It’s also a simple one line of code. It will clear out those ViewModels that are shared between Fragments via the Activity

    0 讨论(0)
  • 2020-12-04 19:32

    It seems like it has been already solved in the latest architecture components version.

    ViewModelProvider has a following constructor:

        /**
     * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
     * {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
     *
     * @param owner   a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
     *                retain {@code ViewModels}
     * @param factory a {@code Factory} which will be used to instantiate
     *                new {@code ViewModels}
     */
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
    

    Which, in case of Fragment, would use scoped ViewModelStore.

    androidx.fragment.app.Fragment#getViewModelStore

        /**
     * Returns the {@link ViewModelStore} associated with this Fragment
     * <p>
     * Overriding this method is no longer supported and this method will be made
     * <code>final</code> in a future version of Fragment.
     *
     * @return a {@code ViewModelStore}
     * @throws IllegalStateException if called before the Fragment is attached i.e., before
     * onAttach().
     */
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
    

    androidx.fragment.app.FragmentManagerViewModel#getViewModelStore

        @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
    
    0 讨论(0)
提交回复
热议问题