An overview of the
This article continues with Jetpack, specifically the ViewModel that implements the MVVM architecture. This example will use ViewModel and LiveData together, and then analyze the ViewModel source code. LiveData will be analyzed in the next article.
In the MVP infrastructure setup example in the previous article, the MVP architecture was used to implement the network loading process. This time we will upgrade the MVP architecture to the MVVM, removing the Presenter and Model modules from the previous example and leaving only the network loading engine. MVVM is then implemented by combining ViewModel and LiveData.
1, use,
Lead-in dependency:
Implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.2.0' implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.2.0'Copy the code
We need to know that the ViewModel is an abstract class that we need to implement. So let’s create an implementation class:
public class DataViewModel extends ViewModel {
// LiveData
private MutableLiveData<String> mLiveData;
// Network loading engine
private RetrofitRequestInterface retrofit;
public DataViewModel(a){
// Comment 1, initialize LiveData
mLiveData = new MutableLiveData();
// Note 2, initialize the network engine
retrofit = RetrofitInstance.getRetrofitInstance().create(RetrofitRequestInterface.class);
}
public LiveData getLiveData(a){
return mLiveData;
}
public void getData(String url){
// Note 3, network request
Call<ResponseBody> call = retrofit.getRequest(url);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String result = response.body().string();
// Comment 4, with LiveData callback
mLiveData.postValue(result);
} catch(IOException e) { e.printStackTrace(); mLiveData.postValue(e.getMessage()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) { mLiveData.postValue(t.getMessage()); }}); }}Copy the code
Above we created a ViewModel implementation class that acts somewhat like MVP’s Presenter. Comments 1 and 2 above create the LiveData object and the network engine, respectively, perform the network load on the local outside call in comment 3, and then load the results with the LiveData callback in comment 4.
Here’s how the interface is called:
public class MainActivity extends AppCompatActivity {
private Button getData;
private DataViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getData = findViewById(R.id.id_get_data);
init();
}
private void init(a){
// Note 5, Factory design pattern creates ViewModel objects
mViewModel = new ViewModelProvider(this).get(DataViewModel.class);
// Set the data callback with the LiveData object
mViewModel.getLiveData().observe(this.new Observer<String>() {
@Override
public void onChanged(String s) {
Toast.makeText(getApplicationContext(), "mag = "+ s, Toast.LENGTH_SHORT).show(); }}); getData.setOnClickListener((View view) -> {// Network request
mViewModel.getData("https://www.baidu.com"); }); }}Copy the code
The above steps create the ViewModel object, set up the data callback, and execute the network request. This is the basic implementation of MVVM. Demo: the ViewModel
Let’s start with two questions:
1. Why use ViewModelProvider to create a ViewModel object instead of creating a new one? ViewModel can avoid memory leaks.
Let’s analyze the ViewModel source code.
2. Source code analysis
See the ViewModelProvider constructor and get() method above:
// ViewModelProvider.java
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
/ / comment 6, obtained by owner ViewModelStore getDefaultViewModelProviderFactory factory object
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: ViewModelProvider.NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull ViewModelProvider.Factory factory) {
// Note 7, save Factory Factory and cache object, ViewModelStore
mFactory = factory;
mViewModelStore = store;
}
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); .return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// Note 9, fetch the ViewModel object from the cache mViewModelStore
/ / the Key value is "androidx. Lifecycle. ViewModelProvider. DefaultKey" + name of the class
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
......
// Objects of the response class are returned directly
return(T) viewModel; }... .// Note 10: There is no ViewModel object in the cache, create it with a factory and store it in the cache
viewModel = (mFactory).create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
Copy the code
In the constructor of the ViewModelProvider overload in comments 6 and 7 above, Call the Activity respectively getViewModelStore () and getDefaultViewModelProviderFactory () method to obtain or create a ViewModel object cache object ViewModelStore and factory Factory and save it. Inside the cache object ViewModelStore is a HashMap that caches ViewModel objects as key-value pairs.
Before creating a ViewModel object, the ViewModelProvider retrieves it from the cache ViewModelStore. The key value is “androidx. Lifecycle. ViewModelProvider. DefaultKey” + name of the class. When the object is not in the cache, it is created with the Factory, cached and returned.
Factory is an abstract class, implementation class object from the upper 6 through Activity getDefaultViewModelProviderFactory () method. Let’s look at how this method for ComponentActivity creates an object for the factory implementation class:
// ComponentActivity.java
public ViewModelProvider.Factory getDefaultViewModelProviderFactory(a) {...if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
/ / comment 11, access to the Application object, create SavedStateViewModelFactory object
getApplication(),
this, getIntent() ! =null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
// SavedStateViewModelFactory.java
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
// Note 12, Create AndroidViewModelFactory
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {...// Note 13, create a ViewModel object from AndroidViewModelFactory
returnmFactory.create(modelClass); . }Copy the code
Above comments at 11, ComponentActivity gained the Application object, created a subclass of the Factory SavedStateViewModelFactory object. Note 12 SavedStateViewModelFactory then to application another as a parameter to create a Factory implementation class AndroidViewModelFactory object, create ViewModel object and the object.
AndroidViewModelFactory AndroidViewModelFactory
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static ViewModelProvider.AndroidViewModelFactory sInstance;
@NonNull
public static ViewModelProvider.AndroidViewModelFactory getInstance(@NonNull Application application) {
// Note 14, singleton pattern to create factory instances
if (sInstance == null) {
sInstance = new ViewModelProvider.AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
// Pass in the Application object
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
// Note 15, The factory method design pattern creates a ViewModel object that holds a reference to Application
returnmodelClass.getConstructor(Application.class).newInstance(mApplication); }... }return super.create(modelClass); }}Copy the code
The factory named AndroidViewModelFactory above is created using the singleton pattern in note 14, and then the ViewModel object is created using the factory method design pattern in note 15. It is worth noting that the ViewModel is created with the mApplication parameter passed in. That is, the ViewModel object does not end up holding a reference to the Activity, but to the Application.
After the above analysis, we should now be able to answer the above two questions:
1. The ViewModel object is obtained using the ViewModelProvider instead of new directly, because the ViewModelProvider has a caching mechanism, and internally uses HashMap to cache the ViewModel object in the form of key-value pairs. A factory is used to create objects that are not in the cache.
2. The ViewModel object does not hold a reference to the Activity, but to the Application. This is one way to avoid Activity memory leaks.
At the end
If you study with me, you can pay attention to my public account, ❤️ program ape Development Center ❤️, and we will share technology regularly every week. Join me and learn together!