ViewModelソース分析

12089 ワード

ViewModelって何?ViewModelはActivityとFragmentのデータを管理するクラスで、ActivityとFragmentとは独立しており、ActivityとFragmentが画面回転などで破棄されても、ActivityとFragmentが再作成されれば元のViewModelと再バインドできます.まず、ViewModelの取得方法を確認します.val viewModel = ViewModelProviders.of(target, factory).get(PlanViewModel::class.java) ViewModelProviders.of()メソッドは、ViewModelProviderのインスタンスを取得し、get()メソッドによりViewModel,ViewModelProviderを取得する.get()は以下のように実現される.
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.
            }
        }

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

ViewModelProvider.get(key,class)メソッドは、取得するviewmodelインスタンスがすでにViewModelStroeに存在するかどうかを先に問い合せる方法であり、インスタンスがすでにViewModelStoreに存在する場合は、前のインスタンスを返し、そうでない場合はViewModelProvider.FactoryはviewModelインスタンスを作成し、viewModelに保存します.次にこのタイプのviewModelを取得するときは、同じViewModelStroeであれば、キャッシュされたviewModelを返すことができます.では、Activityが再作成されてもviewModelを取得するか、それとも前のView Modelを取得するかをどのように保証しますか?ViewModelStroe内部はMapのみでViewModelのキャッシュを管理しているため、Activity破棄再構築後、ViewModelStroeが同じであることを保証するだけで、元のViewModelが入手できることを保証する.ViewModelStroeはどこから来ましたか?引き続き見てof()の実装:
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
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(activity.getViewModelStore(), factory);
    }

ofメソッドの実装により、ViewModelStoreは、ViewModelStoreOwnerを実装したActivityとFragmentによって提供され、以下の2つの部分に分けてActivityとFragmentの実装を表示する.

ActivityがViewModelStoreをどのように管理するか


Activityはすべて再作成されましたが、activityはどのようにしてView ModelStoreを保証しますか?それとも元のものですか?次はActivity.getViewModelStroeの実装:
ComponentActivity.java
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はNonConfiguration InstancesによってViewModelStoreの管理を実現し、最後のNonConfiguration Instancesインスタンスを取得した後、NonConfiguration Instancesを表示する.自分に価値を与えるviewModelStoreとはActivityが再構築前のViewModelStoreを破棄することです
public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

ここまで来るとまたNonConfigurationInstancesが見えますが、ここはちょっと迷います.getViewModelStoreのNonConfigurationInstancesはComponentActivityです.NonConfigurationInstances,getLastNonConfigurationInstanceにあるのはActivity.NonConfigurationInstances,ComponentActivity.NonConfigurationInstancesはViewModelStoreを管理し、Activity.NonConfigurationInstancesはComponentActivityを管理する.NonConfigurationInstances.次にgetLastNonConfigurationInstance()を見続けますが、このmLastNonConfigurationInstancesはどこで作成されたのでしょうか?それは...attach()ではパラメータとして外部から伝達される.activity.attch()はActivity Thread.performLaunchActivity()で呼び出されます.
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
        ...
        return activity;
}

追跡を続ける牙列缺损LastNonConfigurationInstancesはActivity ClientRecordに由来する.A l e s t NonConfigurationInstancesでは、Activity ClientRecord.LastNonConfigurationInstancesはいつ付与されたのですか?答えはactivityが破壊された時、私たちはActivity Threadを見続けた.performDestroyActivity():
ActivityThread.performDestroyActivity() {
    if (getNonConfigInstance) {
                try {
                    r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to retain activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
}

...PerformDestroyActivity()でActivity.retainNonConfigurationInstances()戻り値はActivity ClientRecordに割り当てられます.义齿retainNonConfigurationInstances():
NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap children = onRetainNonConfigurationChildInstances();
        // Fragment mNonConfig , Fragment
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }

retainNonConfigurationInstances()では、Activityを返します.NonConfigurationInstanceの例で、よく知られているコードが表示されます.Object activity = onRetainNonConfigurationInstance(); nci.activity = activity; get LastNonConfigurationInstance()メソッドに戻ると、return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;はonRetainNonConfigurationInstance()がViewModelStoreを管理するComponentActivityを返すことを知っています.NonConfigurationInstanceオブジェクト、次にその実装を見ます.
@Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

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

onRetainNonConfigurationInstance()は、ViewModelStroeを持つComponentActivityを作成しました.NonConfigurationInstanceオブジェクトは、Activity破棄再作成を追跡し、ViewModelは変わらないプロセスを維持します.まとめ:1、Activity破棄時にActivity.retainNonConfigurationInstances()Activityを作成する.NonConfigurationInstaceオブジェクトはActivity ClientRcordに渡されて保存され、Activity.NoConfigurationInstance.义齿NonConfigurationInstanceオブジェクト、ComponentActivity.NonConfigurationInstanceは、ViewModelStore 2、Activity作成後、Activityを持つ.attch()では、Activity ClientRcordが持つActivity.NonConfiguration InstaceオブジェクトはActivityに割り当てられます.mLastNonConfigurationInstances 3は、ViewModelProvidersを呼び出す.of()の場合、ComponentActivityが呼び出されます.getViewModelStore()は、getViewModelStore()で2のmLastNonConfigurationInstancesでComponentActivityを取得します.NonConfigurationInstanceオブジェクト、およびComponentActivity.NonConfigurationInstanceが所有するViewModelStoreが返されます.これによりActivity破棄の再作成が保証され、ViewModelStoreはその破棄前のViewModelStoreなので、ViewModelProviders.of(target, factory).get(ViewModel.class)で取得したのもActivity破棄前のViewModelです.

FragmentがViewModelStoreをどのように管理するか


Fragment.getViewModelStore()は、次のように実装されます.
public ViewModelStore getViewModelStore() {
       if (mFragmentManager == null) {
           throw new IllegalStateException("Can't access ViewModels from detached fragment");
       }
       return mFragmentManager.getViewModelStore(this);
   }

fragment自身はView Modelを直接管理せず、エージェントFragmentManagement.を通じてgetView Model()はView Modelに戻り、次にFragmentMangerを見ます.getViewModelの実装:FragmentManagerImpl.getViewModel()
ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }

FragmentManagerImplはまたmNonConfigを通じてView ModelStoreを管理し、mNonConfigがいつ作成されたかを見てみましょう.
public void attachController(@NonNull FragmentHostCallback host,
                                 @NonNull FragmentContainer container, @Nullable Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }

FragmentがサブFragmentであるか、ActivityがViewModelStoreOwnerを実現しているかによって、1、FragmentルートFragmentでActivityがViewModelStoreOwnerインタフェースを実現する場合、Activity.getViewModelStoreはviewModelStoreを取得します.上記の分析から、このviewModelStoreは再作成前のviewModelStoreを破棄していることがわかります.mNonConfigはViewModelなので、viewModelStoreで取得したmNonConfigも再構築前のmNonCofigを破棄しています.2、fragmentが子fragmentの場合、mNonConfigは親FragmentのmNonConfigから取得し、activityがViewModelStoreOwnerを実現した場合、1に戻り、mNonCofigは破棄前のmNonCofigである.ActivityがViewModelStoreOwnerを実装していない場合は、3に戻ります.3.ActivityがViewModelStoreOwnerを実装場合、新しいmNonConfigを新規作成する.mNonConfigは破棄前のmNonConfigであるため、mNonConfigによって管理されるViewModelStoreも当然破棄前のViewModelStoreである.このことからfragmentのviewModeStoreを保証する際に破棄する前のViewModelStoreはActivityの実現に依存していることがわかる.