前言
jetpack架构组件为界面控制器提供了 ViewModel辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留ViewModel对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。jetpack生命周期如下图
Fragment之间改变数据
- 假设我有三个fragment,HomeFragment、DashboardFragment、NotificationsFragment;现在想在NotificationsFragment中获取HomeFragment中的HomeModel并改变它的数据,此时应该怎么做呢?
- Google官方实际给了一个在 Fragment 之间共享数据案例
- 但是我打算换个思路去做。如果在NotificationsFragment能够获取到HomeModel是不是就可以操作?于是我们可以在NotificationsFragment写出这样的代码
private lateinit var homeViewModel: HomeViewModel;
homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
此时我们打印会发现homeViewModel和HomeFragment中的homeViewModel地址不一样,也就是说不是一个对象。那么应该怎么解决呢?我们先来分析下源码
基本使用
依赖
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
使用
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
/* homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)*/
val root = inflater.inflate(R.layout.fragment_home, container, false)
val binding = DataBindingUtil.bind(root)
homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
binding?.data = homeViewModel
val textView: TextView = root.findViewById(R.id.text_home)
binding?.lifecycleOwner=this
return root
}
}
首先我们来看ViewModelProvider源码
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
- 1、首先分析owner.getViewModelStore()
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
@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;
}
f.who实际是生成随机数
String mWho = UUID.randomUUID().toString();
ViewModelStore实际是个map集合
private final HashMap mMap = new HashMap<>();
所以fragment不一样生成的随机数不一样,那么viewModelStore肯定也不一样。
回到ViewModelProvider源码分析:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
- 2、分析ViewModelProvider后面的代码
owner instanceof HasDefaultViewModelProviderFactory此时是true,所以最终会走到
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
requireActivity().getApplication(),
this,
getArguments());
}
return mDefaultFactory;
}
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
mFactory 是通过AndroidViewModelFactory来创建Factory对象
mDefaultFactory 实际是通过new SavedStateViewModelFactory对象。既然是new的对象,那么他们的内存地址肯定不一样,也就是说mFactory和 mViewModelStore都不一样,既然mFactory和mViewModelStore都不一样,那么通过ViewModelProvider来get获取数据肯定也不一样
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
所以我们就能知道,刚开始我们做的案例,为什么它的对象是不一样的,理由很简单:Fragment不一样创建的ViewModelProvider的mViewModelStore不一样获取对象自然而然不一样。
- 3、我们再来分析以下get来看看viewModel的作用是什么
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)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
此时的ViewModelStore实际就是Map集合,key这可以理解就是class的getCanonicalName
private final HashMap mMap = new HashMap<>();
我们上面知道最终是通过看AndroidViewModelFactory来创建对象,所以我们来看AndroidViewModelFactory
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);
}
实际是通过反射创建ViewModel并传入Application
总结
我们来总结下
- 1、首先我们会创建一个mFactory和mViewModelStore
- 2、fragment不一样创建的mFactory和mViewModelStore都不一样
- 3、mViewModelStore实际一个map集合,key实际是类的getCanonicalName,value是viewModelStore
- 4、获取数首先是通过mViewModelStore来获取,如果有直接取,如果没有则通过反射来获取ViewModel并放入到mViewModelStore
回到刚开始的问题,既然我们知道数据是通过mViewModelStore来获取,我们如果mViewModeStrore和mFactory一直是一个,那么fragment的homemodel不就是一样了吗?解决办法,在Application中提供创建viewModel对象
class App : Application() {
companion object {
lateinit var application: Application
var viewStore: ViewModelStore= ViewModelStore()
fun of(): ViewModelProvider {
val factory = AndroidViewModelFactory.getInstance(application)
return ViewModelProvider(viewStore,factory)
}
}
override fun onCreate() {
super.onCreate()
application = this
}
}
在NotificationsFragment 就可以这样来动态修改HomeFragment的数据了
class NotificationsFragment : Fragment() {
private lateinit var notificationsViewModel: NotificationsViewModel
private lateinit var homeViewModel: HomeViewModel;
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
notificationsViewModel =
ViewModelProvider(this).get(NotificationsViewModel::class.java)
homeViewModel = App.of().get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_notifications, container, false)
val textView: TextView = root.findViewById(R.id.text_notifications)
notificationsViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
Log.e("TAG", Thread.currentThread().name)
homeViewModel.text= MutableLiveData().apply {
value = "Come from NotificationsFragment"
}
return root
}
}
ViewModel是如何在Activity异常销毁重建的时候保持数据不变的
我们直接来看FragmentActivity的getViewModelStore的源码
public ViewModelStore getViewModelStore() {
return FragmentActivity.this.getViewModelStore();
}
//实际是ComponentActivity的getViewModelStore方法
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;
}
实际是通过NonConfigurationInstances来获取mViewModeStore,那么NonConfiguration什么时候存放viewModelStore我们可以跟上去看看
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
Activity的retainNonConfigurationInstances
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap children = onRetainNonConfigurationChildInstances();
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;
}
继续看actvity是怎么创建的,此时回到了ComponentActivity
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// 这里实际是null
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;
}
这时候我们就会发现实际是最终在ComponentActivity的onRetainNonConfigurationInstance中存取数据
那么retainNonConfigurationInstances在哪里被调用的呢,其实我们可以猜测下,既然是销毁后保存数据,肯定会走到ActivityThread中的performDestroyActivity
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
//代码省略
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
}
ViewModel何时被回收?
我们来看AppCompatActivity源码
public AppCompatActivity() {
super();
}
public FragmentActivity() {
super();
}
实际走的还是ComponentActivity
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
当监听到生命周期是ON_DESTROY的时候,判断是否是翻转,不是就清空
public class ViewModelStore {
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
很简单的代码,实际就是清空所有数据