Android的ViewModel

前言

通过懒加载创建model

kotlin 复制代码
private val model by lazy {
    ViewModelProvider(this)[BaseViewModel::class.java]
}
class BaseViewModel : ViewModel() {

}

ViewmodelProvider实现:

kotlin 复制代码
public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
// 创建Factory
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
    if (owner is HasDefaultViewModelProviderFactory)
        owner.defaultViewModelProviderFactory else instance

用this的viewModelStore,创建Factory. this是传给ViewModelProvider的一个ViewModelStoreOwner接口的实现。

kotlin 复制代码
interface ViewModelStoreOwner {
    /**
     * The owned [ViewModelStore]
     */
    val viewModelStore: ViewModelStore
}

ComponentActivity/Fragment实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory。 ComponentActivity实现:

java 复制代码
@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.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

getLastNonConfigurationInstance没有就会new一个返回。 Fragment()实现:

implementation 'androidx.fragment:fragment-ktx:1.6.2'

java 复制代码
public ViewModelStore getViewModelStore() {
    if (mFragmentManager == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (getMinimumMaxLifecycleState() == Lifecycle.State.INITIALIZED.ordinal()) {
        throw new IllegalStateException("Calling getViewModelStore() before a Fragment "
                + "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is not "
                + "supported");
    }
    return mFragmentManager.getViewModelStore(this);
}

 void performAttach() {
   //省略部分代码
     mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);
    }

FragmentManager:

java 复制代码
@NonNull
private FragmentManagerViewModel mNonConfig;
ViewModelStore getViewModelStore(@NonNull Fragment f) {
    return mNonConfig.getViewModelStore(f);
}


void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
 //省略部分代码
 
   if (parent != null) {
   // fragment如果嵌套了,则去找getChildNonConfig
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
        // 获取host的ViewModelStore,自己创建一个ViewModelProvider缓存FragmentManagerViewModel类进去
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
        // 自己huost找不到,自己创建一个FragmentManagerViewModel,传入flase,表示不自动缓存
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
        
        mNonConfig.setIsStateSaved(isStateSaved());
// 设置setNonConfig()
        mFragmentStore.setNonConfig(mNonConfig);

FragmentManagerViewModel:

java 复制代码
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    if (viewModelStore == null) {
        viewModelStore = new ViewModelStore();
        mViewModelStores.put(f.mWho, viewModelStore);
    }
    return viewModelStore;
}

@NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

FragmentController

java 复制代码
public void attachHost(@Nullable Fragment parent) {
    mHost.mFragmentManager.attachController(
            mHost, mHost /*container*/, parent);
}

public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) {
    return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
}

FragmentActivity:

java 复制代码
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

 final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

//内部类
 class HostCallbacks extends FragmentHostCallback<FragmentActivity>{
    // 省略部分代码
      @NonNull
        @Override
        public ViewModelStore getViewModelStore() {
            return FragmentActivity.this.getViewModelStore();
        }
 }

Fragment的ViewModel其实是委托给了FragmentManager。 Fragment李德mNonConfig.getViewModelStore(f),最后调用host.getViewModelStore()。host是FragmentActivity一个内部类。调用到FragmentActivity.this.getViewModelStore()。FragmentActivity继承了ComponentActivity。Fragment调用的是ComponentActivity的getViewModelStore()构建ViewModelProvider返回。如果不行,自己创建一个FragmentManagerViewModel(fasle)用于处理。 ViewModelStore:

kotlin 复制代码
open class ViewModelStore {

    private val map = mutableMapOf<String, ViewModel>()

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun put(key: String, viewModel: ViewModel) {
        val oldViewModel = map.put(key, viewModel)
        oldViewModel?.onCleared()
    }

    /**
     * Returns the `ViewModel` mapped to the given `key` or null if none exists.
     */
    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    operator fun get(key: String): ViewModel? {
        return map[key]
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun keys(): Set<String> {
        return HashSet(map.keys)
    }

    /**
     * Clears internal storage and notifies `ViewModel`s that they are no longer used.
     */
    fun clear() {
        for (vm in map.values) {
            vm.clear()
        }
        map.clear()
    }
}

ViewModelStore是一个map,能存ViewModel和清除。 ViewModelProvider:

kotlin 复制代码
 @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    }

    @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel!!)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key

        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

拿传入的类名canonicalName前拼接DEAFULT_KEY,如果当前store有,且是当前类的实例,强转返回。没有就factory创建。最后调用also缓存进去。 ComonentActivity:

java 复制代码
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    if (mDefaultFactory == null) {
        mDefaultFactory = new SavedStateViewModelFactory(
                getApplication(),
                this,
                getIntent() != null ? getIntent().getExtras() : null);
    }
    return mDefaultFactory;
}

Fragment:

java 复制代码
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    if (mFragmentManager == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (mDefaultFactory == null) {
        Application application = null;
        Context appContext = requireContext().getApplicationContext();
        while (appContext instanceof ContextWrapper) {
            if (appContext instanceof Application) {
                application = (Application) appContext;
                break;
            }
            appContext = ((ContextWrapper) appContext).getBaseContext();
        }
        if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
            Log.d(FragmentManager.TAG, "Could not find Application instance from "
                    + "Context " + requireContext().getApplicationContext() + ", you will "
                    + "need CreationExtras to use AndroidViewModel with the default "
                    + "ViewModelProvider.Factory");
        }
        mDefaultFactory = new SavedStateViewModelFactory(
                application,
                this,
                getArguments());
    }
    return mDefaultFactory;
}

上面都返回了SavedStateViewModelFactory。

java 复制代码
  @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // ViewModelProvider calls correct create that support same modelClass with different keys
        // If a developer manually calls this method, there is no "key" in picture, so factory
        // simply uses classname internally as as key.
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return create(canonicalName, modelClass);
    }


 public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
   //判断是否是isAndroidViewModel
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }

判断是否是AndroidViewModel,newInstance(),反射。get()是从缓存拿,没有则反射一个新对象,also缓存进去。 Fragment正常情况下用FragmentActivity的ViewModelStore。FragmentActivity的父类ComponentActivity

java 复制代码
public ComponentActivity() {

  //省略部分代码
getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                // activity 销毁了
                if (event == Lifecycle.Event.ON_DESTROY) {
                    mContextAwareHelper.clearAvailableContext();
                    // 判断是否是屏幕旋转等情况下发生了
                    if (!isChangingConfigurations()) {
                        // 配置没发生变化,则正常清理
                        getViewModelStore().clear();
                    }
                }
            }
        });
}

在ON_DESTROY时候,判断!isChangingConfigurations(),配置没变化,就真正销毁,调用了getViewModelStore().clear();ViewModel不建议持有Context,因为onDestory后才执行清理ViewModel,所以旋转屏幕ViewModel不会丢数据,虽然走了onDestory内部判断了是否旋转屏幕的判断。

Fragment管理ViewModel

Fragment通过this拿ViewModel,两种情况。一种拿FragmentActivity的ViewModelStore。一种自己构建了FragmentManangerViewModel();这个mNonConfig塞进了mFragmentStore。

java 复制代码
 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);
        }
        // Ensure that the state is in sync with FragmentManager
        mNonConfig.setIsStateSaved(isStateSaved());
        mFragmentStore.setNonConfig(mNonConfig);

FragmentManager中:

java 复制代码
 private final FragmentStore mFragmentStore = new FragmentStore();
  FragmentManagerViewModel getNonConfig() {
        return mNonConfig;
    }

FragmentManager:

java 复制代码
private void clearBackStackStateViewModels() {
        boolean shouldClear;
        if (mHost instanceof ViewModelStoreOwner) {
            shouldClear = mFragmentStore.getNonConfig().isCleared();
        } else if (mHost.getContext() instanceof Activity) {
            Activity activity = (Activity) mHost.getContext();
            shouldClear = !activity.isChangingConfigurations();
        } else {
            shouldClear = true;
        }
        if (shouldClear) {
            for (BackStackState backStackState : mBackStackStates.values()) {
                for (String who : backStackState.mFragments) {
                   mFragmentStore.getNonConfig().clearNonConfigState(who, false);
                }
            }
        }
    }
     void dispatchDestroy() {
        mDestroyed = true;
        execPendingActions(true);
        endAnimatingAwayFragments();
        clearBackStackStateViewModels();
        //省略代码
        }

判断哪些Fragment需要清理。 FragmentController:

java 复制代码
public void dispatchDestroyView() {
        mHost.mFragmentManager.dispatchDestroyView();
    }

FragmentActivity:

java 复制代码
 @Override
    protected void onDestroy() {
        super.onDestroy();
        mFragments.dispatchDestroy();
       mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }

FragmentActivity在onDestory调用FragmentController的mFragment.dispatchDestory()->FragmentManager.dispatchDestory->clearBackStackStateViewModels()。FragmentActivity销毁才销毁ViewModel。 FragmentStateManager重新构建时,也会销毁清理对应的ViewModel。

总结

ViewModel保存数据,页面变化能缓存。自动管理,页面销毁自动清理。Fragment和Activity可共用。Fragment可以拿到Activity的ViewModel(只看传入的类名,this)。 ComponentActivity监听onDestroy,清理。Fragment在FragmentActivity的onDestroy会清理。ViewModel就是一个map。通过内部factory反射实现。判断配置发生变化,如:旋转屏幕,等到真正销毁才清空。

相关推荐
雨白7 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹9 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭11 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日12 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安12 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑12 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟16 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0018 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体