Mvvm
Model–View–ViewModel (数据双向绑定)
model: 数据层
view: 视图层
Viewmodel: 数据和视图逻辑处理
架构原则
https://developer.android.google.cn/jetpack/docs/guide
分离关注点
基于界面的类(Actitivy fragment)应仅包含处理界面和操作系统交互的逻辑
通过模型驱动界面
模型是负责处理应用数据的组件 和view和应用组件独立
持久性是理想之选,原因如下:
如果 Android 操作系统销毁应用以释放资源,用户不会丢失数据。
当网络连接不稳定或不可用时,应用会继续工作。
为了实现mvvm,andorid推出了相应的组件
DataBinding ViewModel LiveData,下面分别介绍一下:
ViewModel
- ViewModel数据不受系统配置更改影响(如在旋转设备时重新创建 Activity)
- ViewModel不是单纯的数据类
- ViewModel为组件提供数据,共享数据
- ViewModel包含数据处理逻辑
- ViewModel转发用户请求来修改数据(获取网络,数据库数据)
https://developer.android.google.cn/topic/libraries/architecture/viewmodel
viewmodel生命周期
- ViewModel的简单使用:
class StudentViewModel : ViewModel() {
var age = 21
var name = "王牡丹"
fun change(){
age++
name = "王甜甜"
}
}
Activity中
var student=ViewModelProvider(this).get(StudentViewModel::class.java)
点击事件改变student属性,屏幕旋转数据不会丢失
下面看下源码,ViewModel的源码很简单,并没有生命周期的处理,只做了清理数据和给数据打标签
public abstract class ViewModel {
// Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
@Nullable
private final Map mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
/* 当ViewModel不再使用并将被销毁时,将调用此方法。
*可以用来清除数据,防止防止这个视图模型的泄漏*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
//因为clear()是final,所以这个方法仍然在模拟对象上调用
//在这些情况下,mBagOfTags是null。但它永远是空的
//因为setTagIfAbsent和getTag不是final,所以我们可以跳过
//清除它
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
@SuppressWarnings("unchecked")
T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {
// It is possible that we'll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// "if the stream is already closed then invoking this method has no effect." (c)
closeWithRuntimeException(result);
}
return result;
}
/**
* Returns the tag associated with this viewmodel and the specified key.
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
T getTag(String key) {
if (mBagOfTags == null) {
return null;
}
synchronized (mBagOfTags) {
return (T) mBagOfTags.get(key);
}
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
下面看一下获取ViewModel的ViewModelProvider源码:
构造函数:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
可以看到,构造函数只是给两个变量赋了值,没做其他操作
ViewModelStore源码,可以看出,只是一个Map
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);
}
Set keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
Factory一个接口,用来生成ViewModel
public interface Factory {
@NonNull
T create(@NonNull Class modelClass);
}
下面看get()方法
public T get(@NonNull Class modelClass) {
...
//key = "androidx.lifecycle.ViewModelProvider.DefaultKey"+class的规范名
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
//如果存在就直接在store中获取,如果没有就创建,并put进去
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来自Activity,我们看一下Activity源码:
首先,我们看到了实现了了关于ViewModel的接口
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,···
在以往的积累里,在数据切换时会有onRetainCustomNonConfigurationInstance这个函数被调用
2020-10-22 14:02:41.129 11026-11026/? E/BaseActivity: onCreate
2020-10-22 14:02:41.321 11026-11026/? E/BaseActivity: onStart
2020-10-22 14:02:41.343 11026-11026/? E/BaseActivity: onResume
选装屏幕
2020-10-22 14:02:53.897 11026-11026/com.example.testjetpack E/BaseActivity: onPause
2020-10-22 14:02:53.906 11026-11026/com.example.testjetpack E/BaseActivity: onStop
2020-10-22 14:02:53.906 11026-11026/com.example.testjetpack E/BaseActivity: onRetainCustomNonConfigurationInstance
2020-10-22 14:02:53.908 11026-11026/com.example.testjetpack E/BaseActivity: onDestroy
2020-10-22 14:02:53.955 11026-11026/com.example.testjetpack E/BaseActivity: onCreate
2020-10-22 14:02:54.020 11026-11026/com.example.testjetpack E/BaseActivity: onStart
2020-10-22 14:02:54.031 11026-11026/com.example.testjetpack E/BaseActivity: onResume
源码中可以看到,这个方法已经废弃
/**
* Use this instead of {@link #onRetainNonConfigurationInstance()}.
* Retrieve later with {@link #getLastCustomNonConfigurationInstance()}.
*
* @deprecated Use a {@link androidx.lifecycle.ViewModel} to store non config state.
*/
@Deprecated
@Nullable
public Object onRetainCustomNonConfigurationInstance() {
return null;
}
我们继续看他被调用的地方,这是一个final方法,不能被继承,它的注释告诉我们如果想保持自己的配置状态请使用ViewModel
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
//为了兼容老版本
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == 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;
}
看到这里,有一些自己的思考,如果想要数据不受生命周期影响,那就需要是系统级别的储存数据(Application ActivityThead)
下面看一下ViewModelStore的由来:
//由getViewModelStore()从NonConfigurationInstances惰性地重新创建
private ViewModelStore mViewModelStore;
//只是有几个变量,没有任何方法
//Activity中
static final class NonConfigurationInstances {
Object activity;//其实这个就是我们保存的数据
HashMap children;
FragmentManagerNonConfig fragments;
ArrayMap loaders;
VoiceInteractor voiceInteractor;
}
//ComponentActivity
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
那么看下它的使用位置:发现在attach()中被初始化
Activity中:
NonConfigurationInstances mLastNonConfigurationInstances;
final void attach(Context context, ActivityThread aThread, ...
NonConfigurationInstances lastNonConfigurationInstances...
) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
···
}
虽然没看到ViewModelStore被创建,但是可以看到储存它的NonConfigurationInstances是系统级别的
继续跟NonConfigurationInstances的走向(debug调试)
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
//第一次进入这个应该是null
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;
}
看下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;
}
getViewModelStore()应该是被系统级别调用的,下面为ViewModel的整个走向
既然ViewModel是系统级别的,那么在ondestory()的时候
如何保障只有在配置改变的时候才会保留,非配置改变则被清除
清除的逻辑并没有在ondestory()中,而是在构造函数的时候监听了生命周期变化,源码如下:
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
......
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();
}
}
}
});
......
}
Livedata:
LiveData :是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。
这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
简单使用:当点击的时候,名字会发生变化
class NameViewModel : ViewModel(){
val currentName: MutableLiveData by lazy{//延迟初始化
MutableLiveData()
}
}
Activity中使用
private NameViewModel model;
model = new ViewModelProvider(this).get(NameViewModel.class);
final Observer nameObserver = new Observer(){
@Override
public void onChanged(@Nullable final String newName){
nameTextView.setText(newName);
}
};
//注册观察者
model.getCurrentName().observe(this,nameObserver);
//livedata数据发生变化,触发nameObserver
button.setOnClickListener { v-> model.currentName.value = "司凤" }
源码分析
MutableLiveData 继承 LiveData源码
1. observe(activity,nameObserver)方法:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
assertMainThread("observe");
//如果生命周期destroyed 不添加
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
//储存观察者
owner.getLifecycle().addObserver(wrapper);
}
参数1 LifecycleOwner是一个接口,而Activity继承此接口,具体实现方法在 FragmentActivity中。
(ComponentActivity也有实现 ,用于那些没有切换到Fragments 1.1.0的应用程序行为由androidx.activity.ComponentActivity提供。)
public interface LifecycleOwner {
@NonNull
Lifecycle getLifecycle();
}
ComponentActivity中代码,FragmentActivity中逻辑相同
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
observe方法,就是将观察者储存,已待后续使用。
2. 接下来看触发观察者的方法:
button.setOnClickListener { v-> model.currentName.value = "司凤" }
LiveData 中 setvalue()方法
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
dispatchingValue(null)
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
considerNotify()
private void considerNotify(ObserverWrapper observer) {
//当前observer不活跃,就是不更新
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//调用了onChanged方法
observer.mObserver.onChanged((T) mData);
}
以上是当数据变化可观察到实现原理
3. LiveData 仅更新处于活跃生命周期状态的应用组件观察者。生命周期感知能力是在哪里体现的?
其实上面源码中,有两处关于生命周期的判断
1.注册的时候
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
...
//如果生命周期destroyed 不添加
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
...
}
2.触发事件的时候
private void considerNotify(ObserverWrapper observer) {
//当前observer不活跃,就是不更新
if (!observer.mActive) {
return;
}
}
LiveData官网介绍中有着一段话:
https://developer.android.google.cn/reference/androidx/lifecycle/LifecycleOwner
https://developer.android.google.cn/reference/androidx/lifecycle/Lifecycle
https://developer.android.google.cn/reference/androidx/lifecycle/Lifecycle.State#DESTROYED
您可以注册与实现LifecycleOwner接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle对象的状态变为 DESTROYED() 时,便可移除此观察者。 这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData
对象而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。
( model.getCurrentName().observe(activity,nameObserver);)
下面具体看下lifecycleOwner的实现
上面又说到LifecycleOwner是一个接口,而Activity实现,那LifecycleOwner是个什么呢?
主要的实现点都在 LifecycleOwner Lifecycle getLifecycle(),
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
LifecycleRegistry extends Lifecycle
FragmentActivity 这个是常用的
final LifecycleRegistry mFragmentLifecycleRegistry = new LifecycleRegistry(this);
下面看一下Lifeycle源码
public abstract class Lifecycle {
/**
* Lifecycle coroutines extensions stashes the CoroutineScope into this field.
*
* @hide used by lifecycle-common-ktx
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
AtomicReference
生命周期状态的改变:
在FragmentActitivty中,每个节点调用下面代码,更改状态 mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
一个Activity 对应一个 mObserverMap,也就是当前的变化,只通知当前页面?
解决mvp 内存泄漏 可以用livedata
问题livedata 如何和Retrofit一起使用
1.我们可以把livedata作为参数传入,使用livedata.setValue()把数据设置进去
- retrofit 返回值返回livedata(默认是Call) ,自定义calladapter
DataBinding
https://developer.android.google.cn/jetpack/androidx/releases/databinding
https://developer.android.google.cn/topic/libraries/data-binding
开启dataBinding
android {
...
dataBinding {
enabled = true
}
android {
...
buildFeatures {
dataBinding true
}
}
简单使用:
Activity中
var binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding)
//绑定数据,注意此处只是单向绑定,数据变化,xml中不会变更
//此类型的对象拥有永不改变的数据(最好)
binding.student = getStudent()
activity_data_binding.xml
......
其中ActivityDataBindingBinding为编译期间自动生成,其位置在
在Fragment RecyclerView中可以这样:
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
双向绑定,使用可观察到数据(这个如果和LiveData一起使用,功能有点重复,但是使用方向上也有些区别,实战没有,暂时无法感知)
var binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding)
//一个纯净的student改变 ui并不会跟着改变,需要继承 BaseObservable 或者 参数需要是Observable类型
var student = Student(ObservableField("王甜甜"), ObservableInt(21))
binding.student = student
binding.button.setOnClickListener {
//一定要使用set方法,直接=无效
student.name.set("王一博")
}
未完待续
注解
自定义方法名称
@BindMethods 映射类中的方法和属性名字,自定义方法的实现,也可以改变现有类的方法和属性的对应关系
注解声明的位置可以是任意类(但是type不要写Activity,会有问题 报错找不到ActivityBindingImpl类)
@BindingMethods({
@BindingMethod(type = TextView.class,
attribute = "android:test",
method = "setText"),
})
public class LoginActivity extends BaseActivity{}
TextView类,将xml android:test 对应其setText方法
自定义处理逻辑 (使用了DataBindingComponent,就不能在其他地方定义@BindingAdapter)
@BindingAdapter
1.基础使用
@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom())
}
参数1: 持有属性的View
参数2: 属性的取值
- 多个属性值处理
Activity中:
@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
Picasso.get().load(url).error(error).into(view)
}
xml布局:
事件处理脚本只能与具有一种抽象方法的接口或抽象类一起使用
@BindingAdapter(
"android:onViewDetachedFromWindow",
"android:onViewAttachedToWindow",
requireAll = false//以指定并非必须为每个属性都分配绑定表达式
)
自定义转换: 属性值或许不是我们需要的类型,需要转换
https://www.jianshu.com/p/9bd2f3069d2e
DataBinding 源码分析
DataBindingUtils
public static T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
//activity设置布局文件
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
错误:
view must have a tag
因为的的布局 总布局是在BaseActivityWithtitle中
@Override
public void setContentView(int layoutResID) {
int layoutId = layoutResID;
if (layoutId != -1) {
LinearLayout content_view = (LinearLayout) View.inflate(this, R.layout.base_title_bar, null);
fl_root_content = content_view.findViewById(R.id.fl_root_content);
fl_root_content.addView(View.inflate(this, layoutId, null));
//contentView并不是我们的布局,而是R.layout.base_title_bar,这个被共有,所以出错
setContentView(content_view);
initTitleBar();
}
livedata只更新具有活跃生命周期的
Observable 会更新全部