Android Jetpack Components of ViewModel study notes

Android Jetpack Components of Lifecycle study notes

Android Jetpack Components of LiveData study notes

Android Jetpack Components of ViewModel study notes

 

Demo Site: https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo

ViewModel Google Docs: https://developer.android.google.cn/topic/libraries/architecture/viewmodel

 

Environment configuration:

It is slightly different from Lifecycle and LiveData. On the basis of it, you also need to import dependencies:

// viewmodel 需要添加的配置
implementation 'android.arch.lifecycle:extensions:1.1.1'

What is ViewModel?

The design purpose of the ViewModel class is to store and manage UI-related data in a life-cycle-aware manner. The ViewModel class allows data to survive configuration changes (such as screen rotation).

 

In my understanding, it is summarized as:

1. ViewModel can realize data sharing between multiple Fragments under the same Activity object.

2. ViewModel can realize data sharing before and after the mobile phone screen is rotated, avoiding unnecessary repeated data requests.

 

Let's start with the use of Demo. Still the LiveData Demo of the previous article:

public class DataUtil {

    private MutableLiveData<String> name = new MutableLiveData<>();

    public LiveData<String> getNameLiveData(){
        return name;
    }

    public void requestHead() {
        // 模拟请求网络或 DB,然后更新数据
        name.setValue("requestHead success");
    }

    public void requestList() {
        // 模拟请求网络或 DB,然后更新数据
        name.setValue("requestList success");
    }

}
public class LiveDataActivity extends AppCompatActivity {

    private DataUtil dataUtil;

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

        dataUtil = new DataUtil();
        dataUtil.getNameLiveData().observe(this, data -> {
            // 得到数据更新



        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        dataUtil.requestHead();
        dataUtil.requestList();
    }
}

The above code actually has a problem. If DataUtil is repeatedly created, the MutableLiveData object will be recreated correspondingly, so data sharing cannot be realized. Don't dig into the horns, look at the usage of ViewModel:

public class ListViewModel extends ViewModel {
    private MutableLiveData<String> headLiveData;
    private MutableLiveData<String> listLiveData;

    public LiveData<String> getHeadLiveData() {
        if (headLiveData == null) {
            headLiveData = new MutableLiveData<>();
        }
        return headLiveData;
    }

    public LiveData<String> getListLiveData() {
        if (listLiveData == null) {
            listLiveData = new MutableLiveData<>();
        }
        return listLiveData;
    }

    public void requestHead() {
        // TODO: 2019-07-29
        headLiveData.setValue("head request success");
    }

    public void requestList() {
        // TODO: 2019-07-29
        listLiveData.setValue("list request success");
    }
}
public class ViewModelActivity extends AppCompatActivity {

    private ListViewModel listViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
        listViewModel.getHeadLiveData().observe(this, data->{
            // 头部数据更新

        });
        listViewModel.getListLiveData().observe(this, data->{
            // list 数据更新

        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        listViewModel.requestHead();
        listViewModel.requestList();
    }
}

Code description:

DataUtil logic changed to ListViewModel, and then inherited ViewModel class. There is no essential change in the others.

In ViewModelActivity, the only change is that the way the ListViewModel is created has changed to:

listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);

 

 

Let's analyze the ViewModel class first:

public abstract class ViewModel {
    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

This is an abstract class, in which there is only one method onCleared(), which is called back to clean up resources when the Activity where the ViewModel is located is destroyed.

ViewModel has a more commonly used subclass AndroidViewModel, which provides application objects:

/**
 * Application context aware {@link ViewModel}.
 * <p>
 * Subclasses must have a constructor which accepts {@link Application} as the only parameter.
 * <p>
 */
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;
    }
}

 

 

Let's go back and analyze the method of obtaining ViewModel:

ViewModelProviders.of(this).get(ListViewModel.class);

First trace the get() method:

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

Continue to trace the get() overload method:

@NonNull
    @MainThread
    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.
            }
        }

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

If the passed ViewModel is already cached, the cached ViewModel object is returned directly; otherwise, a new object is created.

So how does mViewModelStore achieve caching?

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        this.mViewModelStore = store;
    }

It is found that mViewModelStore is assigned in the constructor of ViewModelProvider. Traced to the of method of ViewModelProviders:

@NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

Retrieve to the of method of ViewModelStores:

@NonNull
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }

Find the holderFragmentFor() method in HolderFragment:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

In fact, seeing here, you can basically see the principle:

Create a HolderFragment object in Activity and add it to FragmentManager. Save the ViewModel collection in HolderFragment.

Therefore, when the Activity is destroyed, the HolderFragment will also be destroyed synchronously.

So why is the ViewModel not destroyed before and after the phone screen is rotated?

Tracing the code, the constructor of the HolderFragment class:

public HolderFragment() {
        setRetainInstance(true);
    }
setRetainInstance(true); 这行代码应该是设置避免在屏幕旋转时重建 Fragment。

 

So, to summarize the characteristics of ViewModel:

The ViewModel is attached to the HolderFragment and saved in the FragmentManager of the Activity. Obtaining ViewModel through ViewModelProviders.of(this).get(xxx) at any location under the same Activity and any Fragment will be the same object, so repeated requests are avoided and data sharing is realized.

Due to the characteristics of HolderFragment, data loss after screen rotation can be avoided, and repeated requests can be avoided.

 

 

Demo Site: https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo

 

 

 

 

 

Guess you like

Origin blog.csdn.net/fesdgasdgasdg/article/details/100121615