ViewModelの使用と原理解析
31231 ワード
本文はandroidxに基づいている.Lifecycle:lifecycle-extensions:2.0.0のソースコードを分析する
ViewModelは、ActivityおよびFragmentのデータを管理するためのライフサイクル意識でユーザインタフェースに関するデータを格納管理することを目的とする.FragmentとFragmentとの間の通信などを扱うこともできる.
ActivityまたはFragmentが関連付けられたViewModelを作成すると、ActivityまたはFragmentがアクティブである限り、Activity画面が回転するときに再構築されたとしても、ViewModelは破棄する.データの一時保存にも使えます
ViewModelは主にActivity/fragmentに必要なデータを取得または保持するためのものであり、開発者はActivity/fragmentでViewModelにおけるデータの変更を観察することができる(ここではLiveDataと併せて食べる必要がある).
ps:View ModelはUIのデータを管理するためのもので、View、Activity、Fragmentの参照(メモリの漏洩に注意)を持たせないでください.
本稿では、ViewModelを浅く深く学ぶ
Userデータクラス を定義するそして私たちの今日の主役ViewModel を引き出しますこのときActivityではViewModelが使用できる.実は1つのコードが簡単にインスタンス化され、ViewModelを使用することができます.
この時、ボタン(userの中のageが15になった)をクリックして、携帯電話の画面を回転することができます(この時はActivityが再作成された、つまりonCreate()メソッドが再呼び出されましたが、ViewModelは実際に再作成されていませんか、それともその前のViewModelですか)が、回転するとTextViewに表示されているageが15なのか、、、、、これがViewModelの魔性です.これはView Modelのライフサイクルと言わざるを得ません.Activityが破棄された後でしか自動的に破棄されません(だからView ModelにActivityリファレンスを持たせないでください.メモリが漏れます).Googleの公式画像を引用し、ViewModelのライフサイクルを徹底的に示します.
ViewModelがあれば、ActivityとFragmentはActivityに依存しているため、Activityをインスタンス化するときにActivityをViewModelProvidersに転送し、Activityが作成したViewModelにアクセスしやすくなります.ActivityでuserModelのデータを修正すると、そのFragmentは更新後のデータを取得することができる.
例を見てみましょう(Google公式例)まずViewModelを定義し、そこにデータ を配置します.それからMasterFragmentとDetailFragmentでこのViewModelを手に入れることができて、このViewModelを手に入れると中のデータを手に入れることができて、間接的にViewModelを通じて通信することに相当します.so easy....
また私たちがよく知っているソースの解析の一環に着きました
次のコードからstart.
ViewModelProvidersのof()関数は、実は私たちがViewModelProviderを構築するのに便利です.ViewModelProviderは、名前を見ると何をしているのかがわかります.ViewModelを提供しています.
FactoryはViewModelProviderの内部インタフェースであり、その実装クラスはViewModelインスタンスを構築するために使用される.その中には1つの方法しかありません.ViewModelを作成することです.
Factoryには2つの実装クラスがあります.1つはNewInstanceFactory、1つはAndroid View ModelFactoryです. NewInstanceFactoryソース
NewInstanceFactoryは、その構造方法にパラメータのないclassをインスタンス化するために使用され、View ModelにはContextがない、newInstance()によってインスタンス化される. AndroidViewModelFactoryソース
AndroidViewModelFactoryは、その構造方法にパラメータがあるclassをインスタンス化するために使用され、ViewModelにはContextがある可能性がある. newInstance(アプリケーション)によってインスタンス化する.アプリケーションパラメータがある場合は、このようにして をインスタンス化する.アプリケーションパラメータを持たなければ、newInstance()メソッドを使用してインスタンスを構築する.
AndroidViewModelFactoryは、構造方法でViewModelにApplicationを持ち込むことで、ViewModelでContextを入手することができます.Applicationはアプリのグローバルなので、メモリの漏洩の問題はありません.一部のViewModelではContextリファレンスが必要ですが、メモリの漏洩が心配されています.
次は引き続きof(this)メソッドは分析を続けましょう.最後の
Androidの縦横画面切り替え時にonSaveInstanceState()がトリガーされ、復元時にonRestoreInstanceState()が呼び出されますが、AndroidのActivityクラスにはonReainNonConfigurationInstance()とgetLastNonConfigurationInstance()の2つのメソッドがあります.
この2つの見知らぬ方法を具体的に見てみましょう
getLastNonConfigurationInstance()の呼び出しタイミングを見てみましょう.
まさか、Activityは横長切り替え時にviewModelStoreをこっそり保存し、NonConfigurationInstancesインスタンスに入れたが、横長切り替え時に保存して戻ってきた.
次に、ViewModelコードを構築する後半に来ます.それはViewModelProviderのget()メソッドです.実装を見てみましょう.実は簡単です.
基本的な考え方は一つのkeyを利用してViewModelをキャッシュし、キャッシュがあればキャッシュし、なければ再構築することである.構築時に使用するfactoryは、上のof()メソッドのfactoryである.
上のいくつかの場所ではViewModelStoreが使われていますが、実は一般的なViewModelを保存するクラスです.
ViewModelStoreにはHashMap専用のストレージがありますが、普通でしょう.
いつ呼び出されたclear()を見てみましょう.
ViewModelがライフサイクルで感知されている以上、いつViewModelを整理すればいいのでしょうか.
FragmentActivityのonDestroy()メソッドに来て、ここで整理されていることを発見しました.
多くの友达が聞くかもしれませんが、ViewModelはいったい何ですか?
実はとても简単で、1つの抽象的な类だけあって、中は1つの空の方法???私は拭いて、長い間やって、もとはView Modelは主役ではありません....
View ModelにはAndroid View Modelというサブクラスがあります.Applicationのプロパティが入っています.それだけで、ViewModelでContextを使いやすいようにしています.
ViewModelのソースコードは実は多くなく、理解しやすいです.主に公式FragmentActivityが技術実装を提供しています.onRetainNonConfigurationInstance()は状態を保存し、getLastNonConfigurationInstance()は回復します.
Activityにはこんな2つのものがあったのか、以前はonSaveInstanceState()とonRestoreInstanceState()を知っていただけで、ポーズを上げました.
ViewModelは、ActivityおよびFragmentのデータを管理するためのライフサイクル意識でユーザインタフェースに関するデータを格納管理することを目的とする.FragmentとFragmentとの間の通信などを扱うこともできる.
ActivityまたはFragmentが関連付けられたViewModelを作成すると、ActivityまたはFragmentがアクティブである限り、Activity画面が回転するときに再構築されたとしても、ViewModelは破棄する.データの一時保存にも使えます
ViewModelは主にActivity/fragmentに必要なデータを取得または保持するためのものであり、開発者はActivity/fragmentでViewModelにおけるデータの変更を観察することができる(ここではLiveDataと併せて食べる必要がある).
ps:View ModelはUIのデータを管理するためのもので、View、Activity、Fragmentの参照(メモリの漏洩に注意)を持たせないでください.
本稿では、ViewModelを浅く深く学ぶ
一、ViewModelの使用
1.View Modelの導入
// 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"
2.簡単に使う
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 + '\'' +
'}';
}
}
public class UserModel extends ViewModel {
public final MutableLiveData 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);
}
}
}
// 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() {
@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();
}
});
}
}
この時、ボタン(userの中のageが15になった)をクリックして、携帯電話の画面を回転することができます(この時はActivityが再作成された、つまりonCreate()メソッドが再呼び出されましたが、ViewModelは実際に再作成されていませんか、それともその前のViewModelですか)が、回転するとTextViewに表示されているageが15なのか、、、、、これがViewModelの魔性です.これはView Modelのライフサイクルと言わざるを得ません.Activityが破棄された後でしか自動的に破棄されません(だからView ModelにActivityリファレンスを持たせないでください.メモリが漏れます).Googleの公式画像を引用し、ViewModelのライフサイクルを徹底的に示します.
3.ViewModel妙用1:ActivityとFragmentの「通信」
ViewModelがあれば、ActivityとFragmentはActivityに依存しているため、Activityをインスタンス化するときにActivityをViewModelProvidersに転送し、Activityが作成したViewModelにアクセスしやすくなります.ActivityでuserModelのデータを修正すると、そのFragmentは更新後のデータを取得することができる.
public class MyFragment extends Fragment {
public void onStart() {
// ViewModel , Activity
UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
}
}
4.ViewModel妙用2:FragmentとFragmentの「通信」
例を見てみましょう(Google公式例)
public class SharedViewModel extends ViewModel {
private final MutableLiveData- selected = new MutableLiveData
- ();
public void select(Item item) {
selected.setValue(item);
}
public LiveData
- 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.
});
}
}
二、ViewModelソースコード解析
また私たちがよく知っているソースの解析の一環に着きました
次のコードからstart.
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
ViewModelProviders.of(this)
に従って新しい世界の扉を開けます1. 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のof()関数は、実は私たちがViewModelProviderを構築するのに便利です.ViewModelProviderは、名前を見ると何をしているのかがわかります.ViewModelを提供しています.
FactoryはViewModelProviderの内部インタフェースであり、その実装クラスはViewModelインスタンスを構築するために使用される.その中には1つの方法しかありません.ViewModelを作成することです.
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
*
*
* @param modelClass a {@code Class} whose instance is requested
* @param The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
T create(@NonNull Class modelClass) ;
}
Factoryには2つの実装クラスがあります.1つはNewInstanceFactory、1つはAndroid View ModelFactoryです.
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public T create(@NonNull Class 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は、その構造方法にパラメータのないclassをインスタンス化するために使用され、View ModelにはContextがない、newInstance()によってインスタンス化される.
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 create(@NonNull Class 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は、その構造方法にパラメータがあるclassをインスタンス化するために使用され、ViewModelにはContextがある可能性がある.
AndroidViewModelFactoryは、構造方法でViewModelにApplicationを持ち込むことで、ViewModelでContextを入手することができます.Applicationはアプリのグローバルなので、メモリの漏洩の問題はありません.一部のViewModelではContextリファレンスが必要ですが、メモリの漏洩が心配されています.
次は引き続きof(this)メソッドは分析を続けましょう.最後の
new ViewModelProvider(activity.getViewModelStore(), factory);
の最初のパラメータはactivityのgetView ModelStore()メソッドを呼び出します(このメソッドはView ModelStoreを返します.このクラスはView Modelを格納します.以下に説明します).ここのactivityはandroidxです.fragment.app.FragmentActivity、このgetViewModelStore()メソッドを見てみましょう/**
* 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の縦横画面切り替え時にonSaveInstanceState()がトリガーされ、復元時にonRestoreInstanceState()が呼び出されますが、AndroidのActivityクラスにはonReainNonConfigurationInstance()とgetLastNonConfigurationInstance()の2つのメソッドがあります.
この2つの見知らぬ方法を具体的に見てみましょう
/**
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;
}
getLastNonConfigurationInstance()の呼び出しタイミングを見てみましょう.
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
......
}
まさか、Activityは横長切り替え時にviewModelStoreをこっそり保存し、NonConfigurationInstancesインスタンスに入れたが、横長切り替え時に保存して戻ってきた.
2. viewModelProvider.get(UserModel.class)
次に、ViewModelコードを構築する後半に来ます.それはViewModelProviderのget()メソッドです.実装を見てみましょう.実は簡単です.
public T get(@NonNull Class 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 get(@NonNull String key, @NonNull Class 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;
}
基本的な考え方は一つのkeyを利用してViewModelをキャッシュし、キャッシュがあればキャッシュし、なければ再構築することである.構築時に使用するfactoryは、上のof()メソッドのfactoryである.
3. ViewModelStore
上のいくつかの場所ではViewModelStoreが使われていますが、実は一般的なViewModelを保存するクラスです.
public class ViewModelStore {
private final HashMap 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にはHashMap専用のストレージがありますが、普通でしょう.
いつ呼び出されたclear()を見てみましょう.
4. ViewModel.onCleared()リソース回収
ViewModelがライフサイクルで感知されている以上、いつViewModelを整理すればいいのでしょうか.
FragmentActivityのonDestroy()メソッドに来て、ここで整理されていることを発見しました.
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
5.ビューモデルを見る
多くの友达が聞くかもしれませんが、ViewModelはいったい何ですか?
public abstract class ViewModel {
/**
* ViewModel ,
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
実はとても简単で、1つの抽象的な类だけあって、中は1つの空の方法???私は拭いて、長い間やって、もとはView Modelは主役ではありません....
6. AndroidViewModel
View ModelにはAndroid View Modelというサブクラスがあります.Applicationのプロパティが入っています.それだけで、ViewModelでContextを使いやすいようにしています.
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 getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
三、まとめ
ViewModelのソースコードは実は多くなく、理解しやすいです.主に公式FragmentActivityが技術実装を提供しています.onRetainNonConfigurationInstance()は状態を保存し、getLastNonConfigurationInstance()は回復します.
Activityにはこんな2つのものがあったのか、以前はonSaveInstanceState()とonRestoreInstanceState()を知っていただけで、ポーズを上げました.