Principles and core components using Jetpack, ViewModel resolution

Foreword

ViewModel awareness of life cycle intended to store and manage data related to the user interface, which can be used to manage data in the Activity and Fragment. It may also be used to handle communications between Fragment Fragment and the like.

When the Activity or Fragment create a ViewModel associated, so long as the Activity or Fragment is active, then the ViewModel will not be destroyed, even when the reconstruction of the Activity screen rotation. So it can be used to do the data is temporarily stored.

ViewModel is mainly used to obtain or retain data Activity / Fragment needed, developers can change the ViewModel observations in the Activity / Fragment in (It should be eaten with LiveData).

UI ViewModel only used to manage the data, do not let it hold View, Activity or Fragment references (be careful memory leaks).

In this paper, progressive approach to the way learning ViewModel.

Use the ViewModel

Introduced ViewModel

//引入AndroidX吧,替换掉support包
implementation 'androidx.appcompat:appcompat:1.0.2'

def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

Simple to use

User define a data class.

class User implements Serializable {

    public int age;
    public String name;

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

Then leads to today's hero ViewModel.

public class UserModel extends ViewModel {

    public final MutableLiveData<User> mUserLiveData = new MutableLiveData<>();

    public UserModel() {
        //模拟从网络加载用户信息
        mUserLiveData.postValue(new User(1, "name1"));
    }

    //模拟 进行一些数据骚操作
    public void doSomething() {
        User user = mUserLiveData.getValue();
        if (user != null) {
            user.age = 15;
            user.name = "name15";
            mUserLiveData.setValue(user);
        }
    }

}

This time you can use a ViewModel in an Activity. In fact, a simple example of the code, then you can use the ViewModel.

//这些东西我是引入的androidx下面的
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;

public class MainActivity extends FragmentActivity {

    private TextView mContentTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mContentTv = findViewById(R.id.tv_content);

        //构建ViewModel实例
        final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);

        //让TextView观察ViewModel中数据的变化,并实时展示
        userModel.mUserLiveData.observe(this, new Observer<User>() {
            @Override
            public void onChanged(User user) {
                mContentTv.setText(user.toString());
            }
        });

        findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //点击按钮  更新User数据  观察TextView变化
                userModel.doSomething();
            }
        });
    }
}

This time, we click of a button (user becomes of age 15), we can rotate the phone screen (when in fact this Activity is re-created, and that is onCreate () method is called again, but in fact ViewModel is not re-created or before the ViewModel), but when we rotate, we found that age is actually displayed on the TextView 15, which is the ViewModel magic lies. The ViewModel would have to mention the life cycle, and it is only after the destruction of Activity, it will self-destruct (so do not let ViewModel holds Activity quote ah, will memory leaks). The following quote about the Google official pictures of the life cycle of the ViewModel show the most.
Principles and core components using Jetpack, ViewModel resolution

ViewModel Magical 1: Activity Fragment "communicate" with

With ViewModel, Activity and Fragment can share a ViewModel, because Fragment is dependent on the Activity of the Activity incoming ViewModelProviders when instantiating the ViewModel, it will give you a good've created the Activity of ViewModel, this may Fragment convenient access to the data in the ViewModel. Activity userModel modified data, the number of Fragment can get updated.

ViewModel Magical 2: Fragment Fragment "communicate" with

Let's look at an example (Google official example)

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}
  1. First define a ViewModel, put some data in it.

2, and then MasterFragment DetailFragment the ViewModel can get, get inside the ViewModel can get the data, corresponding to the indirect communication via the ViewModel. so easy ...

Source resolve ViewModel

We start from this code below.

final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);

We open the door to a new world to follow ViewModelProviders.of (this).

ViewModelProviders.of(this) 方法

/**
 * 用于构建一个ViewModelProvider,当Activity是alive时它会保留所有的该Activity对应的ViewModels.
 */
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    return of(activity, null);
}

@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    //检查application是否为空,不为空则接收
    Application application = checkApplication(activity);
    if (factory == null) {
        //构建一个ViewModelProvider.AndroidViewModelFactory
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

ViewModelProviders inside of () function is to facilitate the fact that we build a ViewModelProvider. And ViewModelProvider, a look to know the name of doing, is providing the ViewModel.

Factory ViewModelProvider is an internal interface, and its implementation class is used to construct ViewModel instance. It there is only one way is to create a ViewModel.

/**
 * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
 */
public interface Factory {
    /**
     * Creates a new instance of the given {@code Class}.
     * <p>
     *
     * @param modelClass a {@code Class} whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Factory implementation class has two: one is NewInstanceFactory, one AndroidViewModelFactory.

NewInstanceFactory source

public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

NewInstanceFactory designed to instantiate that there will be no argument constructor class, and which is without ViewModel Context, and then it is through newInstance () to instantiate a.

AndroidViewModelFactory source

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    /**
     * Retrieve a singleton instance of AndroidViewModelFactory.
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @return A valid {@link AndroidViewModelFactory}
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    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) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

AndroidViewModelFactory designed to instantiate that class constructor parameters inside, and there may be ViewModel with the Context.

It is through newInstance (application) to be instantiated. If there is argument with this application instantiation.

If no application with parameters, then still go newInstance () method to build instance.

AndroidViewModelFactory brought by the constructor to ViewModel Application, you can get in Context ViewModel inside, because APP Application is global, then there is no problem of memory leaks, the perfect solution Context reference needs some ViewModel inside, but they worry about memory leaks problem.

Here we continue ViewModelProviders.of (this) method to continue to analyze it, pay attention to the last sentence of new ViewModelProvider (activity.getViewModelStore (), factory); The first argument will call activity of getViewModelStore () method (this method returns ViewModelStore, this class is used to store the ViewModel, we will discuss below), activity here is androidx.fragment.app.FragmentActivity, look at this getViewModelStore () method.

/**
 * 获取这个Activity相关联的ViewModelStore
 */
@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.");
    }
    if (mViewModelStore == null) {
        //获取最近一次横竖屏切换时保存下来的数据
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

//没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore
//注意,这是FragmentActivity中的NonConfigurationInstances(其实Activity中还定义了一个NonConfigurationInstances,内容要比这个多一些,但是由于没有关系到它,这里就不提及了)
static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
    FragmentManagerNonConfig fragments;
}

Android horizontal and vertical screen is triggered when switching onSaveInstanceState (), which calls onRestoreInstanceState restore (), but there are two methods Android's Activity class called onRetainNonConfigurationInstance () and getLastNonConfigurationInstance () These two methods.

Look at the specific method of the two never met.

/**
 保留所有fragment的状态。你不能自己覆写它!如果要保留自己的状态,请使用onRetainCustomNonConfigurationInstance()
 这个方法在FragmentActivity里面
 */
@Override
public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    return nci;
}

//这个方法在Activity里面,而mLastNonConfigurationInstances.activity实际就是就是上面方法中年的nci
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

Let's look at the opportunity to call getLastNonConfigurationInstance (), and

protected void onCreate(@Nullable Bundle savedInstanceState) {
    ......
    super.onCreate(savedInstanceState);

    NonConfigurationInstances nc =
            (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
        mViewModelStore = nc.viewModelStore;
    }
    ......
}

I did not expect it, Activity at the time had to switch screens quietly saved viewModelStore, placed inside NonConfigurationInstances example, horizontal and vertical screen save resumed when switching back, the equivalent ViewModel instance is still ah, thus avoiding the horizontal and vertical screen when the switch data lost.

viewModelProvider.get(UserModel.class)

Here we come to the ViewModel code phrase to build half of it is ViewModelProvider the get () method to see if realized, is actually very simple.

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");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //先取缓存  有缓存则用缓存
    ViewModel viewModel = mViewModelStore.get(key);

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

    //无缓存  则重新通过mFactory构建
    viewModel = mFactory.create(modelClass);
    //缓存起来
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

The general idea is to use a key to the ViewModel cache, the cache has a cache, it is not reconstructed. factory is used when constructing the top of the factory () method.

ViewModelStore

More than one place above uses ViewModelStore, it is actually an ordinary save ViewModel class.

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);
    }

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

ViewModelStore have a HashMap dedicated to storing ordinary bar.

Let's look clear when called ().

ViewModel.onCleared () resource recovery

Since ViewModel is the life cycle of perception, then when ViewModel should clean it?

We came FragmentActivity of onDestroy () method, found that it is here to clean up.

/**
 * Destroy all fragments.
 */
@Override
protected void onDestroy() {
    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }

    mFragments.dispatchDestroy();
}

Look ViewModel

Many of my friends may have to ask, what in the end is ViewModel?

public abstract class ViewModel {
    /**
     * 这个方法会在ViewModel即将被销毁时调用,可以在这里清理垃圾
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

In fact, very simple, an abstract class, which on an empty method ??? I rub, So now, the original ViewModel not a hero ...

AndroidViewModel

There is a subclass ViewModel is AndroidViewModel. It contains an Application attribute, nothing, in order to facilitate a Context ViewModel inside.

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

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

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}

summary

ViewModel source, not much, easier to understand, the main official FragmentActivity provides technology, onRetainNonConfigurationInstance () to save the state, getLastNonConfigurationInstance () recovery.

There are so original Activity 2 stuff, before I just know onSaveInstanceState () and onRestoreInstanceState (), a rising posture.

Guess you like

Origin blog.51cto.com/14332859/2401440