[AAC] series of four in-depth understanding of architectural components: ViewModel

0. Introduction

This article is an in-depth understanding of "Android Architecture Components' fourth in a series of articles based on AAC 1.1.1 version of the source code

[AAC] Android application architecture a series of new era!

[AAC]-depth understanding of the cornerstone series of two architectural components: Lifecycle

[AAC] series of three deep understanding of architectural components: LiveData

Do not link this point, toxic, do not

In the previous LiveDataprinciple analysis of the article, we mentioned ViewModelthat cooperation with LiveData able to maximize the value to the maximum.

This one, we have to look ViewModel simple terms, use the ViewModel Laijiangjiang, life cycle, as well as its implementation principle.

1. ViewModel Overview

Before diving explain ViewModel, first take a brief under ViewModel:

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModelclass allows data to survive configuration changes such as screen rotations.

ViewModel is designed to manage data associated with UI, and can sense the life cycle; in addition ViewModel can 配置改变make data in the case is preserved. ViewModel focuses on 以感知生命周期的方式the management interface specific data.

We know that change is similar to the configuration items rotate the screen so will result in our Activity to be destroyed and rebuilt, this time Activity held data loss will follow, and ViewModel 则并不会被销毁so can help us save the data in the process, rather than rebuilding Activity after re going to get. And ViewModel allows us 不必去担心潜在的内存泄露问题, at the same time compared to the ViewModel using onSaveInstanceState()methods more advantages, such as relatively large data storage and does not require serialization and de-serialization.

ViewModel In short, a lot of advantages, it follows that we introduce the basic use under the ViewModel.

2. ViewModel basic use of

ViewModel is also very simple to use, Android provides a ViewModel class allows us to inherit, and offers ViewModelProvidersto help us instantiate the ViewModel.

A conveyance official website examples:

A) : first add it relies:

def lifecycle_version = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
复制代码

B) : a custom MyViewModelinherits from ViewModel, and comprises a LiveData:

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}
复制代码

c) : Activity in aid in ViewModelProvidersinstances get the ViewModel, and with the LiveData Subscribe to notify users of changes:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}
复制代码

So simple steps, we will use on the ViewModel, even if MyActivity recreate, MyActivity will get MyViewModel is an example.

So the question is, ViewModel 的生命周期到底是怎么样的呢?

What is the principle behind it hidden it? Next, let's take a look.

3. ViewModel life cycle

We mentioned before, and will not ViewModel Activity configuration change and the destruction of the destruction together, then in the end is kind of how it ViewModel life cycle?

Official website to see a map of:

ViewModel can be seen still alive at the time of reconstruction Activity.

Why

The principle of 4. ViewModel

Under review we used before ViewModel code:

MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
复制代码

Here we start ViewModelProviders.ofmethod to start to see:

//ViewModelProviders
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
  //传入了 null
  return of(activity, null);
}

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
  //检查合法性
  Application application = checkApplication(checkActivity(fragment));
  if (factory == null) {
    //走到这里,返回了 AndroidViewModelFactory 的单例
    factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
  }
  return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
复制代码

ViewModelProviders.of()Methods to help us build a default AndroidViewModelFactoryfactory class, to help create ViewModel, and returned a current Activity in the life cycle ViewModelProvider.

Look AndroidViewModelFactory:

    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
				//单例模式
        private static AndroidViewModelFactory sInstance;
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                  //...
                }
            }
            return super.create(modelClass);
        }
    }
复制代码

AndroidViewModelFactory by a fact 反射constructed ViewModel factory class method, and is a single embodiment.

Look down we found the ViewModel class is passed to the ViewModelProvider.get()method.

Take a look at the get method:

//ViewModelProvider
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
  String canonicalName = modelClass.getCanonicalName();
  if (canonicalName == null) {
    throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
  }
  //每个类都有一个唯一的 key
  return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
  //先从 store 中获取
  ViewModel viewModel = mViewModelStore.get(key);

  if (modelClass.isInstance(viewModel)) {
    //noinspection unchecked
    return (T) viewModel;
  } else {
    //noinspection StatementWithEmptyBody
    if (viewModel != null) {
      // TODO: log a warning.
    }
  }

  viewModel = mFactory.create(modelClass);
  mViewModelStore.put(key, viewModel);
  //noinspection unchecked
  return (T) viewModel;
}
复制代码

Explain the code, it was found that each class have built a ViewModel 唯一的 key, and try to go through this key called ViewModelStorethe class to get the ViewModel instance, if, it will go through to create Factory is null, and the new instance is stored to ViewModelStore.

The main process seems just like we usually do the same cache as if nothing special things, do not see a trace of processing code related with the life cycle , how is this going?

Carefully think about, get priority method will get from this ViewModelStore, then in theory, as long as ViewModelStore this class is not destroyed in the process of configuration changes, then we can ensure that we create ViewModel will not be destroyed , we certainly leak out of important things about ViewModelStore.

A closer look back to the past, ViewModelProvider building does not seem simple:

new ViewModelProvider(ViewModelStores.of(fragment), factory);
复制代码

Here ViewModelStore passed in a class by ViewModelStores created and introduced to fragment, there must be something wrong.

//ViewModelStores
public static ViewModelStore of(@NonNull Fragment fragment) {
  if (fragment instanceof ViewModelStoreOwner) {
    return ((ViewModelStoreOwner) fragment).getViewModelStore();
  }
  return holderFragmentFor(fragment).getViewModelStore();
}
复制代码

Here the addition of several classes out ViewModelStoreOwner, HolderFragment, let's track it down.

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}
复制代码

ViewModelStoreOwner similar with LifecycleOwner, just an interface definition, take a look at the focus holderFragmentFor(fragment)method returns HolderFragment.

//HolderFragment
public static HolderFragment holderFragmentFor(Fragment fragment) {
  return sHolderFragmentManager.holderFragmentFor(fragment);
}
复制代码

The method has come HolderFragmentManager category, how the addition of a HolderFragmentManager, God Fana.

static class HolderFragmentManager {
    private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
    private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();

    private ActivityLifecycleCallbacks mActivityCallbacks =
            new EmptyActivityLifecycleCallbacks() {
                @Override
                public void onActivityDestroyed(Activity activity) {
                    HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
                    if (fragment != null) {
                        Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
                    }
                }
            };

    private boolean mActivityCallbacksIsAdded = false;

    private FragmentLifecycleCallbacks mParentDestroyedCallback =
            new FragmentLifecycleCallbacks() {
                @Override
                public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
                    super.onFragmentDestroyed(fm, parentFragment);
                    HolderFragment fragment = mNotCommittedFragmentHolders.remove(
                            parentFragment);
                    if (fragment != null) {
                        Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
                    }
                }
            };

    void holderFragmentCreated(Fragment holderFragment) {
        Fragment parentFragment = holderFragment.getParentFragment();
        if (parentFragment != null) {
            mNotCommittedFragmentHolders.remove(parentFragment);
            parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
                    mParentDestroyedCallback);
        } else {
            mNotCommittedActivityHolders.remove(holderFragment.getActivity());
        }
    }

    private static HolderFragment findHolderFragment(FragmentManager manager) {
        if (manager.isDestroyed()) {
            throw new IllegalStateException("Can't access ViewModels from onDestroy");
        }

        Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
        if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
            throw new IllegalStateException("Unexpected "
                    + "fragment instance was returned by HOLDER_TAG");
        }
        return (HolderFragment) fragmentByTag;
    }

    private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
        HolderFragment holder = new HolderFragment();
        fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
        return holder;
    }

    HolderFragment holderFragmentFor(FragmentActivity activity) {
        FragmentManager fm = activity.getSupportFragmentManager();
        HolderFragment holder = findHolderFragment(fm);
        if (holder != null) {
            return holder;
        }
        holder = mNotCommittedActivityHolders.get(activity);
        if (holder != null) {
            return holder;
        }

        if (!mActivityCallbacksIsAdded) {
            mActivityCallbacksIsAdded = true;
            activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
        }
        holder = createHolderFragment(fm);
        mNotCommittedActivityHolders.put(activity, holder);
        return holder;
    }

    HolderFragment holderFragmentFor(Fragment parentFragment) {
        FragmentManager fm = parentFragment.getChildFragmentManager();
        HolderFragment holder = findHolderFragment(fm);
        if (holder != null) {
            return holder;
        }
        holder = mNotCommittedFragmentHolders.get(parentFragment);
        if (holder != null) {
            return holder;
        }

        parentFragment.getFragmentManager()
                .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
        holder = createHolderFragment(fm);
        mNotCommittedFragmentHolders.put(parentFragment, holder);
        return holder;
    }
}
复制代码

We can learn from the source code HolderFragmentManageris mainly to do these things:

  1. When we want to get ViewModel instance, we will build a first HolderFragment, it 添加我们的宿主(Activity/Fragment)中and cache it up;

  2. At the same time to listen by registering callback host life cycle, Activity correspondence ActivityLifecycleCallbacks, Fragment correspondence FragmentLifecycleCallbacks, clear the cache when the host destruction;

Like its name, HolderFragmentManageris responsible for managing HolderFragment, see it injected HolderFragment, next look HolderFragment.

HolderFragment Source streamlined as follows:

public class HolderFragment extends Fragment implements ViewModelStoreOwner {
    private static final String LOG_TAG = "ViewModelStores";

    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String HOLDER_TAG =
            "android.arch.lifecycle.state.StateProviderHolderFragment";

    private ViewModelStore mViewModelStore = new ViewModelStore();
		//看这里看这里看这里
    public HolderFragment() {
        setRetainInstance(true);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sHolderFragmentManager.holderFragmentCreated(this);
    }
		...
    @Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }
}
复制代码

Internal HolderFragment hold a ViewModelStoreand realized we mentioned before ViewModelStoreOwnerthe interface, and the most important is the code:

public HolderFragment() {
  setRetainInstance(true);
}
复制代码

Fragment.setRetainInstance(true) The method can achieve the effect is still Activity saved after configuration changes.

Here the principle ViewModel implemented it clear: by injecting a retainInstance to HolderFragment true, the use of properties Fragment to ensure that after the Activity configuration changes still able to survive it, and to ensure the survival of ViewModelStore inside HolderFragment, the ultimate guarantee of internal ViewModelStore ViewModel survival cache storage, enabling ViewModel life cycle characteristics of this function . (Again Fragment!)

5 illustrates ViewModel

Key ViewModel class class diagram:

ViewModel implement the principle sequence diagram:

6. knowledge carding and summary

Key categories to explain :

  • ViewModel Abstract class that is responsible for the preparation and management Activity / Fragment of data, and can also handle communication Activity / Fragment with the outside world, usually stored business logic, similar Presenter; ViewModel often exposed to LiveData Activity / Fragment; configuration and Activity ViewModel change does not result in recovery;
  • AndroidViewModelA will hold Applicationthe ViewModel;
  • ViewModelStore, Is responsible for storing the ViewModel class, and is also responsible for notifying the ViewModel before it is cleared, that is invoked ViewModel.onCleared();
  • ViewModelStoreOwner , Is an abstract "ViewModelStore owner" of the interface definition, similar LifecycleOwner role, it has achieved its HolderFragment, FragmentActivity;
  • HolderFragment, A retainInstanceproperty trueand implements ViewModelStoreOwnerthe Fragment, to save ViewModelStore, and to ensure that it is not destroyed when the configuration changes;
  • HolderFragmentManager , Responsible for creating, injection, caching management HolderFragment work;

ViewModel principle summarized :

By injecting one retainInstanceof truethe HolderFragment, using Fragment features to ensure that after the Activity configuration change is still able to survive and to ensure the survival of ViewModelStore inside HolderFragment, the ultimate guarantee internal ViewModelStore stored ViewModel cache survival, thereby achieving ViewModel disposed Activity function does not destroy the case of change.

ViewModel Note the use of :

  1. 不要持有 Activity : ViewModel will not change the configuration Activity to be destroyed, so definitely do not hold those classes with Activity-related, such as Activity in certain View, let ViewModel holds Activity will lead to memory leaks, but also noted that even Lifecycle too No;
  2. 不能访问 UI: ViewModel should only be responsible for managing data , not to visit the UI, but can not hold it;

7. Summary

ViewModel using the characteristics of Fragment, provide us with a way to manage the UI-related data in a specific life cycle; help us to logical data management peeled off from the Activity / Fragment in.

In fact ViewModel can manage not only data, but also can store the code business logic processing, and also be able to easily communicate with each other in different Fragment Activity, this solves a big problem of communication between us Fragment ever.

After completion of in-depth understanding of Lifecycle, LiveData, ViewModel, you may find that they are indeed very powerful, can effectively help us solve practical problems encountered in the development process.

We highly recommend the experience got in the car, then later buy tickets ah.

8. References and recommended

  1. developer.android.com/topic/libra…

  2. medium.com/androiddeve…

Reproduced in: https: //juejin.im/post/5d0111c1e51d45108126d226

Guess you like

Origin blog.csdn.net/weixin_34208185/article/details/93166197