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);
}
}
}
- 这时候在Activity中就可以使用ViewModel了. 其实就是一句代码简单实例化,然后就可以使用ViewModel了.
//这些东西我是引入的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的魔性所在.这个就不得不提ViewModel的生命周期了,它只有在Activity销毁之后,它才会自动销毁(所以别让ViewModel持有Activity引用啊,会内存泄露的). 下面引用一下谷歌官方的图片,将ViewModel的生命周期展示的淋漓尽致.
3. ViewModel妙用1: Activity与Fragment"通信"
有了ViewModel,Activity与Fragment可以共享一个ViewModel,因为Fragment是依附在Activity上的,在实例化ViewModel时将该Activity传入ViewModelProviders,它会给你一个该Activity已创建好了的ViewModel,这个Fragment可以方便的访问该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,在里面放点数据
-
然后在MasterFragment和DetailFragment都可以拿到该ViewModel,拿到了该ViewModel就可以拿到里面的数据了,相当于间接通过ViewModel通信了. so easy…
二、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实例的.它里面只有一个方法,就是创建一个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个实现类:一个是NewInstanceFactory, 一个是AndroidViewModelFactory .
- NewInstanceFactory源码
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,并且ViewModel里面是不带Context的,然后它是通过newInstance()去实例化的.
- AndroidViewModelFactory 源码
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的.
-
它是通过newInstance(application)去实例化的.如果有带application参数则是这样实例化
-
如果没有带application参数的话,则还是会走newInstance()方法去构建实例.
AndroidViewModelFactory通过构造方法给ViewModel带入Application,就可以在ViewModel里面拿到Context,因为Application是APP全局的,那么不存在内存泄露的问题.完美解决了有些ViewModel里面需要Context引用,但是又担心内存泄露的问题.
下面我们继续ViewModelProviders.of(this)方法继续分析吧,注意最后一句new ViewModelProvider(activity.getViewModelStore(), factory);
第一个参数会调用activity的getViewModelStore()方法(这个方法会返回ViewModelStore,这个类是拿来存储ViewModel的,下面会说到),这里的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类还有2个方法名为onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()这两个方法。
来具体看看这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实例里面,横竖屏切换时保存了又恢复了回来,相当于ViewModel实例就还在啊,也就避免了横竖屏切换时的数据丢失.
2. viewModelProvider.get(UserModel.class)
下面我们来到那句构建ViewModel代码的后半段,它是ViewModelProvider的get()方法,看看实现,其实很简单
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
【附】相关架构及资料
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
[外链图片转存中…(img-z0s4bjeS-1712301262166)]
[外链图片转存中…(img-UtTGpRsK-1712301262166)]
[外链图片转存中…(img-UasEwq2U-1712301262167)]
[外链图片转存中…(img-ajVaf0Sh-1712301262167)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
【附】相关架构及资料
[外链图片转存中…(img-1vs3RzuX-1712301262167)]
[外链图片转存中…(img-UYJ2Cw0S-1712301262168)]
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。