MVP & MVVM
MVP结构
MVP存在的问题:
View ,Model之间的通信都要经过presenter为中介通信,实现方式是接口注入,接口众多的现象
维护成本高,常见的 View点击-》Model更新数据-》更新View,每次至少来回调用两次接口
主要将逻辑分在View和Model中实现,复杂业务中这两个类容易膨胀
MVVM & LiveData
MVVM中的View和Model之间通过ViewModel通信,AndroidX中的LiveData采用观察者模式,Model的更新,可直接被View监听到,不需要用户手动定义接口更新数据,而是自动同步给View实现UI更新
JetpPack总体介绍
2017年的Google I/O大会上,Google推出了一系列譬如 Lifecycle、ViewModel、LiveData等一系列 更适合用于MVVM模式开发 的架构组件。
ViewModel
https://developer.android.com/reference/android/arch/lifecycle/ViewModel.html
如何使用:
public class UserActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity_layout);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.userLiveData.observer(this, new Observer() {
@Override
public void onChanged(@Nullable User data) {
// update ui.
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.doAction();
}
});
}
}
public class UserModel extends ViewModel {
public final LiveData userLiveData = new LiveData<>();
public UserModel() {
// trigger user load.
}
void doAction() {
// depending on the action, do necessary business logic calls and update the
// userLiveData.
}
}
创建逻辑:
通过Activity中的fieldViewModelStore 去管理,以ViewModel的类名为key存储在map中,如果之前map中已经有这个key的数据,那么直接返回,如果没有创建过,则重新创建
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
*
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param key The key to use to identify the ViewModel.
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
@MainThread
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.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
销毁逻辑
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
如果ViewModel中有使用到一些生命周期较长的数据,应在onClear回调中手动销毁
生命周期:
用户在使用产品的过程中,可能随时会被中断或界面发生重建,如果数据的丢失会造成很差的用户体验,当然现在都知道在onSaveInstanceState()中保存数据,在界面重建时恢复数据,这种虽然可以解决问题,但会造成每个界面都要编写板寸和恢复的代码,而且数据量过大时会影响执行性能,VIewModel的出现就是解决这个问题,它会在活动重建时仍然保存数据,在活动创建完成后从中获取数据。
androidx中Activity的ondestroy方法:
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
总结:
数据和界面的分离,使数据驱动界面
解决了运行中断和界面重建时的数据保存问题
实现Activity中Fragment之间的数据交互
配合LiveData实时获取最新数据
LifeCycle
https://developer.android.com/reference/android/arch/lifecycle/Lifecycle
如何使用:
java8:
If you use Java 8 Language, then observe events with DefaultLifecycleObserver. To include it you should add "android.arch.lifecycle:common-java8:
class TestObserver implements DefaultLifecycleObserver {
@Override
public void onCreate(LifecycleOwner owner) {
// your code
}
}
** java7:**
class TestObserver implements LifecycleObserver {
@OnLifecycleEvent(ON_CREATE)
void onCreated(LifecycleOwner source) {}
@OnLifecycleEvent(ON_ANY)
void onAny(LifecycleOwner source, Event event) {}
}
State & Event:
public abstract class Lifecycle {
//注册LifecycleObserver (比如Presenter)
public abstract void addObserver(@NonNull LifecycleObserver observer);
//移除LifecycleObserver
public abstract void removeObserver(@NonNull LifecycleObserver observer);
//获取当前状态
public abstract State getCurrentState();
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY
}
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
}
Event发送时序:
总结:
实现能够感知 Activity/Fragment 生命周期信息的组件,便于页面逻辑的拆解,代码解耦可代码的可读性和可维护性
LiveData的生命周期基于LifeCycle实现
LiveData
https://developer.android.com/reference/androidx/lifecycle/LiveData.html
如何使用:
(1)定义LiveData 变量,对外开放LiveData和修改value的方法:主线程调用setValue,后台线程调用postValue
class ProfileLiveDataViewModel : ViewModel() {
private val _xytestData = MutableLiveData(XyTestData())
val xyTestData: LiveData = _xytestData
fun changeHomeTown() {
_xytestData.value?.apply {
innerData.homeTown = innerData.homeTown + "——湖南"
}
_xytestData.value = _xytestData.value
}
}
(2)触发修改
binding.likeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.onLike();
viewModel.changeHomeTown();
}
});
(3)observer监听LiveData 的回调,onChanged一定 发生在主线程
viewModel.getXyTestData().observe(activity, new Observer() {
@Override
public void onChanged(XyTestData xyTestData) {
Log.d("xiaoyu_activity home : ",xyTestData.innerData.homeTown);
binding.lastname.setText(xyTestData.innerData.homeTown);
}
});
具有生命周期的观察者模式
LiveData 认为观察者的生命周期处于STARTED状态或RESUMED状态下,表示观察者处于活动状态,LiveData只通知活跃的观察者去更新数据
LiveData会在活动处于Destroy时释放观察者,所以开发者无需特别处理
(1)添加监听:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
assertMainThread("observe");
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);
}
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
(2)setValue时去通知observers:
private void considerNotify(ObserverWrapper 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;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
永久生效的观察者模式:
@MainThread
public void observeForever(@NonNull Observer super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true);
}
这种情况下LiveData不会自动释放observer,需要我们手动调用removeObserver释放,避免内存泄漏。
Transformation:
map
val popularity: LiveData = Transformations.map(_likes) {
when {
it > 9 -> Popularity.STAR
it > 4 -> Popularity.POPULAR
else -> Popularity.NORMAL
}
}
switchmap
MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id));
void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}
类似于rxjava中的map和flatMap
LiveData/ MutableLiveData /MediatorLiveData
LiveData只可读,不可修改
MutableLiveData,开放了setValue和PostValue
MediatorLiveData,支持LiveData组合
* MediatorLiveData liveDataMerger = new MediatorLiveData<>();
* liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
* liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
*
* * Let's consider that we only want 10 values emitted by {@code liveData1}, to be * merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code * liveData1} and remove it as a source. *
* liveDataMerger.addSource(liveData1, new Observer() { * private int count = 1; * * {@literal @}Override public void onChanged(@Nullable Integer s) { * count++; * liveDataMerger.setValue(s); * if (count > 10) { * liveDataMerger.removeSource(liveData1); * } * } * });
总结:
- 确保UI与数据状态相匹配
LiveData遵循观察者模式,当生命周期状态发送改变时,LiveData会通知观察者对象,您可以合并代码以更新这些观察者对象中的UI。您的观察者可以在任意变化的情况下更新UI,而不是每次数据发生变更时更新UI。
- 没有内存泄露
观察者被绑定到生命周期对象上,并在其关联的生命周期被销毁后会进行清理。所以不会有内存泄露。
不会因为Activity停止而崩溃
如果观察者的生命周期处于不活动状态下,例如Activity在后台栈中,那不会接收到任意的LiveData事件。
- 不在需要手动管理生命周期
UI组件只需要观察相关的数据,不需要停止和恢复观察。LiveData自动管理这些,因为他知道相关的生命周期的变化。
- 始终保持最新数据
如果一个生命周期变得不活动,它将在再次激活时接收最新的数据。例如,后台的某个Activity在返回到前台后会接收最新的数据。
- 适当的配置更改
如果一个Activity或者Fragment由于配置改变而重新创建,例如设备旋转,它会立即接收到最新的可用数据。
- 共享资源
您可以使用单例模式扩展LiveData对象,以包装系统服务,以便它们可以在您的应用程序中共享。LiveData对象一旦连接到系统服务,然后任何需要资源的观察者都可以看到LiveData对象。
提供了一个数据更新UI,实现通信的非常轻量的方式
DataBinding
如何使用:
(1)gradle配置:
android {...
dataBinding {
enabled true
}}
(2)在xml中直接将UI与ViewModel绑定,监听ViewModel中数据的变化,将变化直接体现在view上
(3)编译后直接根据layout文件生成Binding类
(4)通过binding对象直接获取到各个id的view:
val binding: ObservableFieldProfileBinding =
DataBindingUtil.setContentView(this, R.layout.observable_field_profile)
binding.user = observableFieldProfile
好处:
数据直接展示,没有复杂处理逻辑的,这样的方式可以用,只需要关心更新viewModel中的数据,而不需要关心数据与Ui的绑定了
生成Binding类后,通过binding对象可以直接拿到xml中定义的view对象,比butterKnife和findViewById方便
存在的问题:
编译时期出错,没有生成Binding类
数据在xml中绑定,不方便调试
逻辑维护比较分散
Android 业务层架构思考
说明:
ViewModel中的数据根据数据逻辑拆分成不同的LiveData
UI逻辑根据业务拆分成不同的Observer
-
layout使用include方式拆分
UI与 数据逻辑之间通过ViewModel为中介通信
各个Observer通过LifeCycle感知Fragment/Activity的生命周期,并能够拿到context和viewmodel
各个Observer之间的非通用数据可通过自定义LiveData进行通信
好处:
每个controller都有足够的context,包括Activity或者fragment的lifecycle,ViewModel数据交互;因此能够很容易地完成自己的逻辑
逻辑分离,扩展性和可维护性更好
参考:
https://developer.android.com/jetpack
https://blog.csdn.net/Alexwll/article/details/83302173
https://medium.com/@MinaSamy/android-architecture-components-lifecycle-433ace1ec05d
https://medium.com/exploring-android/exploring-the-new-android-architecture-components-c33b15d89c23
http://timmy6.github.io/2018/08/13/android-lifecycle/
https://proandroiddev.com/5-common-mistakes-when-using-architecture-components-403e9899f4cb
https://codelabs.developers.google.com/codelabs/android-databinding/#0
https://developer.android.com/reference/android/arch/lifecycle/Transformations