官方文档永远是最好的学习资料:
Android Jectpack
Android Jetpack: LiveData 和 Lifecycle 介绍 | 中文教学视频
Android Jetpack - ViewModel | 中文教学视频
Android Jetpack Room | 中文教学视频
深刻了解还需多看文档和源码。java
AAC (Android Architecture Components) 是谷歌推出的一套包含在 Jetpack 中的,帮助咱们构建稳健、可测试且易维护应用的组件库,主要包括 Lifecycle、LiveData、ViewModel、Room、WorkManager 等一系列好用的工具。注意,AAC 并非一种新的架构,只是一套和架构相关的工具,能够帮助你更加简单高效的构建你想要的架构。android
MVC (Model-View-Controller)、MVP (Model-View-Presenter) 和 MVVM (Model-View-ViewModel) 在 Android 中的应用大概能够归纳为下图(架构分层因人而异,这里只是我本身的一些理解)
git
IView
和
IPresenter
之类的接口,而后 View 层和 Presenter 层相互持有对方的引用,这样就存在不少问题,好比:
Activity
已经 onDestroy
,但并无被回收的场景MVVM 采用 View 层与 ViewModel 层的数据绑定的方式,View 层监听相应的数据,并在数据变动时本身更改视图,从而很好地解决了上述问题:github
View
的显示可见 MVVM 更为先进好用,实现 MVVM 的方法也有不少,而 AAC 就是为 MVVM 而生的,经过 AAC 中的 LiveData
和 ViewModel
等组件,咱们能够很容易地在 Android 上实现 MVVM。它的 Lifecycle 组件可让咱们更有效的管理 app 内的各类生命周期,在配置变动时保存数据,避免内存泄漏,更方便地把数据加载到 UI 中;LiveData 用来构建一个能够在数据变动时通知视图的数据对象,且具备生命周期感知的能力;ViewModel 能够存储 UI 相关的数据,并保证在配置变动时不会丢失。数据库
LiveData
是一个可观察的数据持有类,并且它能够感知其余应用组件 (如 Activity
、Fragment
或 Service
) 的生命周期,这种感知可确保 LiveData
仅更新生命周期处于激活状态(STARTED 和 RESUMED)的观察者。它的主要优势有:缓存
LiveData
的最新数据,数据预加载再也不须要考虑 View
的状态Activity
和 Fragment
重建时也会收到通知(须要和 ViewModel
配合使用)简单用法。假设咱们的 MainActivity
的视图中有一个 Button
和一个 TextView
来实现一个简单的计数器,还有一个计数值的 LiveData
。点击 Button
会更新计数值,而 TextView
监听该 LiveData
来更新本身。示例分析以下:网络
public class MainActivity extends AppCompatActivity {
// 一、MutableLiveData 是什么
private MutableLiveData<Integer> mLiveData = new MutableLiveData<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化工做
// ...
mLiveData.setValue(0);
// 二、LiveData.observe() 的参数都是什么意思
// public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
mLiveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable final Integer count) {
// Update the UI, in this case, a TextView.
mTextView.setText(newName);
}
});
// 三、LiveData 的值怎么更新
mButton.setOnClickListener(v -> mLiveData.setValue(mLiveData.getValue() + 1));
}
}
复制代码
一、首先 LiveData
是一个抽象类,且 setValue
和 postValue
两个更新 value 的方法都是 protected
的,而 MutableLiveData
继承 LiveData
,重写这两个方法,并将访问权限设置为 public
;架构
二、public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
第一个参数 LifecycleOwner
是持有生命周期的对象,好比 Activity
和 Fragment
,可让 LiveData
能够感知它的生命周期,并在生命周期结束时将其移除,避免内存泄漏;第二个参数 Observer
是一个接口,它只有一个方法 void onChanged(T t)
,LiveData
会在数据更新时调用这个函数来通知 UI 层的观察者。并发
三、setValue
和 postValue
两个均可以更新 value,不一样之处在于 setValue
只能在主线程调用,而 postValue
能够用于子线程(注意:短期内屡次 postValue
,LiveData
只会保留最后一个来通知观察者)。app
固然,这只是LiveData
用法的简单说明,实际项目中若是这样用会有些问题:
LiveData
来解决这个问题Activity
或 Fragment
会重建致使数据丢失,这个问题固然能够经过 onSaveInstanceState
来保留和恢复数据;LiveData
,单例能够解决这个问题,但数据源不在被须要时也没法回收资源。上面说的这些问题均可以经过某些手段解决,可是都不是很优雅,而谷歌固然考虑到了,这些问题在接下来的 ViewModel
中,都获得了很好的解决。
ViewModel
,顾名思义,是用来存储和管理 View 相关数据的,而 AAC 中的 ViewModel
还能够感知生命周期,能够在配置变动(如屏幕旋转)时自动保存数据,还能够在生命周期真的结束时触发回调来清除没必要要的请求,以避免内存泄漏。而做为 MVVM 的中间层,它还肩负着响应 View 层的动做,以及操做 Model 层请求数据的任务。ViewModel
的生命周期以下图所示:
ViewModel
因为生命周期是长于 View 层(
Activity
,
Fragment
或
View
)的,不能(也不须要)持有 View 层的任何东西,若是要使用 context 能够继承
AndroidViewModel
,它内部持有 Application 的 context。
public class MyViewModel extends ViewModel {
// ...
public List<User> getUsers() {
return users;
}
public void loadUsers() {
// 请求 users 数据.
}
// 一、调用时机
@Override
protected void onCleared() {
// 清除没必要要的请求
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// MyActivity 重建时仍是能拿到同一个 MyViewModel
// 二、ViewModelProviders.of 参数
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.loadUsers();
}
}
复制代码
一、ViewModel
的 onCleared
函数会在持有它的对象的生命周期结束时调用,以避免异步请求形成 ViewModel
的内存泄露;
二、public static ViewModelProvider of(@NonNull FragmentActivity activity)
或 public static ViewModelProvider of(@NonNull Fragment fragment)
,传入的参数能够是 Activity
或 Fragment
,其内部会拿到 Activity
或 Fragment
的 ViewModelStore
,顾名思义就是存储 ViewModel
的地方,其内部也只是一个 HashMap<String, ViewModel>
,键是内部用 ViewModel
的 Class
的名字拼出的字符串。
利用 ViewModelStore
存储 ViewModel
能够十分方便地管理 ViewModel
,前面说的 ViewModel
能够在配置变动后存活,其实就是在重建保存状态时,保存下了 ViewModelStore
,实现方式和保存 Fragment
相似(Bundle
保存的数据是有限的,为了在配置变动时保存大量数据,也能够用 Fragment
来存)。利用 ViewModelStore
存储 ViewModel
的方式还能够方便 Fragment
之间的通信和数据同步,只要多个 Fragment
隶属于 同一个 Activity
,他们就能够经过 Activity
的 ViewModelStore
拿到同一个 ViewModel
。
虽然 LiveData
和 ViewModel
单独拿出来用也是强有力的工具,谷歌推出 AAC 的目的明显不只仅是一个工具,这一整套服务于的架构相关的组件能够帮助咱们轻松的打造 MVVM,并且都带着生命周期感知能力。接下来经过一个计数器例子,简单介绍下使用方法。
public class CountViewModel extends ViewModel {
private final MutableLiveData<Integer> mCountLiveData = new MutableLiveData<Integer>();
public LiveData<Integer> getCountLiveData() { return mCountLiveData; }
public void loadCount() {
// 能够经过网络或数据库请求数据
request.enquen(response -> {
if (response.isSuccess()) {
mCountLiveData.postValue(response.data);
}
});
}
public void countDown() {
// 减少计数
if (mCountLiveData.getValue() != null) {
mCountLiveData.setValue(mCountLiveData.getValue() - 1);
} else {
loadCount();
}
}
public void countUp() {
// 增大计数
if (mCountLiveData.getValue() != null) {
mCountLiveData.setValue(mCountLiveData.getValue() + 1);
} else {
loadCount();
}
}
}
public class ActionFragment extends Fragment {
//...
@Override
public void onViewCreated(View v, Bundle savedInstanceState) {
final CountViewModel countViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
// 改变计数值
v.findViewById(R.id.up_buttonn).setOnClickListener(v -> {
countViewModel.countUp();
});
v.findViewById(R.id.down_buttonn).setOnClickListener(v -> {
countViewModel.countDown();
});
}
//...
}
public class ShowCountFragment extends Fragment {
//...
@Override
public void onViewCreated(View v, Bundle savedInstanceState) {
final TextView count = v.findViewById(R.id.count_text_view);
// 这里的 getActivity() 是为了拿到同一个 CountViewModel
ViewModelProviders.of(getActivity())
.get(CountViewModel.class)
.getCountLiveData()
// 这里的 this 是为了让 LiveData 绑定观察者的生命周期
.observe(this, data -> {
count.setText(data);
});
}
//...
}
复制代码
在该例子中,CountViewModel
中有一个 mCountLiveData
用于保存计数值,还有一组用于更新计数值的方法;ActionFragment
和 ShowCountFragment
位于同一个 Activity
中,这样能够保证二者拿到同一个 CountViewModel
,ActionFragment
的两个按钮用于增减计数,而 ShowCountFragment
则监听并显示计数值。
这样一个简单的 MVVM 架构的计数器就搭建好了。View 层的 ShowCountFragment
绑定 ViewModel 层的 mCountLiveData
,并在数据变动时更新视图,而 ViewModel 层不须要直接持有任何 View 层的引用(LiveData
持有的观察者在 View 层,可是会自动根据生命周期来移除),ViewModel 也不关心监听数据的 View 的数量和类型,View 拿到数据后显示什么东西也都无所谓,多一个 View 只不过是多了一个观察者而已,并且多个 View 不须要借助其余工具(EventBus、RxBus 等事件总线)就能够经过 ViewModel 实现通讯。
对于数据源比较多的场景,谷歌建议咱们单独抽出 Repository 层(其实就是 Model 层)用于处理数据来源(缓存、数据库或网络),并向上返回数据的 LiveData
(如 Room)来保持数据的同步,整个架构图以下图所示:
**配合源码使用,效果更佳!**特别推荐使用 androidx 来看源码,会清晰方便不少。
View 层的动态性很强,各个界面切换、视图元素交替出现等都伴随着生命周期的变化,而下层元素的生命周期每每要长于 View 的生命周期,为了避免形成资源浪费和内存泄漏,咱们时常须要手动管理 View 的生命周期。好比咱们有一个显示当前位置的 Activity
,咱们须要在 onStart
时开始监听位置信息,并在位置变化时更改视图,在 onStop
时注销监听。
Activity
和
Fragment
的生命周期是一件十分繁琐并且低效的事情,为了更有效地管理生命周期,许多第三方库(例如 Glide、RxLifecycle)都将监听与分发生命周期地任务交给
Frgament
,由于只要将
Frgament
塞进
Activity
中,
Frgament
就能与
Activity
生命周期同步,而后经过自定义的
Frgament
将生命周期事件发送出来。AAC 中的 Lifecycle 组件也是经过这种方式和观察者模式实现了生命周期地自动管理。
Lifecycle 组件主要有 Lifecycle
、LifecycleObserver
和一套相关地类组成。Lifecycle
定义了一系列生命周期地状态和事件,其惟一子类 LifecycleRegistry
则实现了一个做为生命周期被观察者所具备的能力,在生命周期变化时向观察者们分发事件。LifecycleObserver
是一个起标识做用地接口,它的子接口 LifecycleEventObserver
的 onStateChanged
方法在 LifecycleRegistry
分发生命周期时会被回调。
如今问题来了:
Lifecycle
Lifecycle
的持有者都会实现 LifecycleOwner
接口,重写 Lifecycle getLifecycle();
方法,返回本身持有的生命周期对象 LifecycleRegistry
,Activity
和 Fragment
都实现了该接口。
Lifecycle
的生命周期事件从哪里来,和以前说的 Frgament
有什么关系?LifecycleOwner
的实现类既然持有 Lifecycle
,那确定就会发送事件啦。Frgament
的话比较简单,内部保存了一个 mLifecycleRegistry
,并在本身的生命周期事件中调用 mLifecycleRegistry
的 handleLifecycleEvent
方法将事件传递给 LifecycleRegistry
。Activity
本身也持有一个 mLifecycleRegistry
,可是它不本身发送事件,而是把任务交给了一个叫 ReportFragment
的 Fragment
,其实现也很简单,就是在本身的生命周期事件中拿到 Activity
的 mLifecycleRegistry
,而后再进行分发。
LifecycleObserver
怎么用须要监听 Lifecycle
的对象实现这个接口,而后在 onStateChanged
方法中根据不一样的生命周期作出相应的动做。好比前面说的监听位置信息的例子,咱们就能够单独抽出一个对象来专门作监听操做,实现 LifecycleObserver
接口,并本身处理生命周期事件。
class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
//...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
复制代码
这里咱们来扒一扒源码,看 ViewModel
是如何建立、如何挺过配置变动,又是什么时候真正的消失的。
ViewModel
的建立前面咱们已经知道 ViewModel
都保存在 ViewModelStore
中,那只要知道 ViewModelStore
如何被建立、保存与销毁就行。在 ComponentActivity
的 getViewModelStore
方法能够看到 ViewModelStore
的建立过程。
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
复制代码
能够看到 ViewModelStore
是从一个叫 NonConfigurationInstances
的实例中拿的,若是拿不到说明以前没经历过配置变动,那就 new 一个出来。接着看
// ComponentActivity 里的
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
复制代码
NonConfigurationInstances
除了保存 ViewModelStore
还存着 custom
用于保存咱们本身定制的数据,这个能够经过重写 onRetainCustomNonConfigurationInstance
方法来用。再来看下 getLastNonConfigurationInstance
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
// 这个是 Activity 中的,和 ComponentActivity 里的那个不同
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
复制代码
是在 Activity
的 NonConfigurationInstances
中的 activity
,经过 NonConfigurationInstances
咱们也能大体看出 Fragment
在配置变动的时候会被保存到 FragmentManagerNonConfig
中。
在 ComponentActivity
的 onRetainNonConfigurationInstance
中会分别拿咱们定制的 custom
和 ViewModelStore
,而后返回建立好的 NonConfigurationInstances
。而 onRetainNonConfigurationInstance
会在配置变动时被 LocalActivityManager
的 dispatchRetainNonConfigurationInstance
方法中调用,从而保存状态信息。至此咱们就知道保存 ViewModelStore
的流程,再继续深刻源码就没法自拔了。
ViewModel
,调用它的 onCleared
getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
复制代码
在 ComponentActivity
的构造函数中有这么一段代码,恰好用到了咱们前面说的 Lifecycle
和 LifecycleObserver
,在 ON_DESTROY 时判断下是不是配置变动,不是的话就调用 ViewModelStore
的 clear
方法,会清除 ViewModelStore
中保存的 ViewModel
,并调用他们的 clear
方法,进而调用到 onCleared
。
至此,Activiy
中的 ViewModel
相关生命周期已经分析完了,Fragment
中也大同小异,主要涉及 FragmentManagerImpl
、FragmentManagerViewModel
等一些类,感兴趣的能够顺着 ViewModelStore
的思路,本身深刻了解下。
观察者模式的那套东西均可以玩一些骚操做,责任链、事件总线什么的,LiveData
做为一个可观察对象,固然也能够,这里简单分析两个。
首先了解下一个叫 MediatorLiveData
的对象,它继承自 MutableLiveData
,经过 public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged)
方法实现了增长其余 LiveData
做为本身事件源的功能,源 LiveData
更新时,会调用传入的 Observer
的 onChanged
方法作处理。MediatorLiveData
是利用 public void observeForever(@NonNull Observer<? super T> observer)
方法来添加源的,这个方法不须要传 LifecycleOwner
,可是须要手动移除观察者,不过不用担忧,MediatorLiveData
已经帮咱们作了。若是 MediatorLiveData
已经没有任何观察者,它会自动调用源 LiveData
的 removeObserver
方法来移除对源 LiveData
的监听,以防本身内存泄漏。
利用 MediatorLiveData
对象,咱们能够作一些事件变换的操做,Transformations
的 map
和 switchMap
就是经过该对象实现的。
以前的事件总线都须要手动处理生命周期的问题,EventBus 须要手动注销,RxBus 须要 RxLifecycle
的扩展库来监听生命周期。有了 LiveData,咱们彻底能够用不多的代码撸一个具备生命周期感知能力的事件总线,实现很简单(一个简单但实用的 LiveDataBus 只须要一百行代码左右),网上也有不少开源的库,这里只讲下大体思路。
要想作事件总线,核心就是发送方和接收方拿到同一个可观察对象,在这里就是同一个 LiveData
。最简单的方法就是定义一个单例类,假设叫 LiveDataBus
,里面放一个 Map<Class<?>, MutableLiveData<?>>
来保存全部的事件类型 Class
和对应的 LiveData
,这样就能经过事件的 Class
拿到对应的惟一 LiveData
。这里有几个须要注意的地方:
Map
的并发问题,由于来拿 LiveData
的可能多个线程的,因此要作好同步工做,再考虑到可见性,建议用 ConcurrentHashMap
;Map
里存的 Class
和 MutableLiveData
都被擦出了泛型(不一样事件的 Class<T>
里泛型确定不同呀),因此要本身保证二者的泛型一致,能够定义一个保存 Class<T>
和 MutableLiveData<T>
的带泛型 <T>
的类,在里面保证一致;LiveDataBus
中的 LiveData
不会被释放,也就致使它里面的数据不会被释放,能够在事件不用时发送一个空事件;注意:ViewModel
已经可以解决不少场合的通讯问题,并且能在不用时释放掉,因此能用 ViewModel
通讯的就不要用事件总线,除非两个 Activity
必须经过某种方式进行通信,也要先考虑下单例的 LiveData
,实在不行再用事件总线。
最后再强调一遍,不要滥用事件总线
LiveData
源码很少,这里只简单分析几个 LiveData
的特性。
从 LiveData
的订阅方法看起
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// ...
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// ...
owner.getLifecycle().addObserver(wrapper);
}
复制代码
能够看到 LiveData
不只是一个可观察对象,同时仍是一位观察者,它所观察的就是 Activity
或 Fragment
等持有 Lifecycle
的 LifecycleOwner
,固然也要把观察本身的观察者保存下来,接下来看下 LifecycleBoundObserver
这个观察者是怎么处理 Lifecycle
的。首先它继承自 ObserverWrapper
这是一个会根据 Lifecycle
是否处于激活状态决定是否分发数据的观察者,这里先跳过,LifecycleBoundObserver
还实现了 GenericLifecycleObserver
接口,其实就是前面说过的 LifecycleEventObserver
,它的生命周期状态回调函数以下:
// LifecycleBoundObserver 的方法
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
// LiveData 的方法
public void removeObserver(@NonNull final Observer<? super T> observer) {
// ...
ObserverWrapper removed = mObservers.remove(observer);
// ...
removed.detachObserver();
removed.activeStateChanged(false);
}
// LifecycleBoundObserver 的方法
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
复制代码
onStateChanged
在判断 Lifecycle
DESTROYED 的时候调用 LiveData
的 removeObserver
,首先将观察 LiveData
的观察者移除,防止内存泄漏,以后再调用LifecycleBoundObserver
的 detachObserver
将本身从 LifecycleOwner
的观察者中移除,自此将相互之间的观察状态接触。
作预加载的时候,咱们能够在 Activity
或 Fragment
建立时直接请求数据,塞到 LiveData
,而后只要生命周期处于激活状态,无论何时监听 LiveData
,都能收到最新的消息。咱们再来看一下 LifecycleBoundObserver
的 onStateChanged
方法,它在最后调用了父类的 activeStateChanged(shouldBeActive())
方法,来大体看一下
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
// 该方法是 LiveData 的
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
// 该方法是 LiveData 的
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
复制代码
它首先会根据当前的激活状态进行去重,而后会根据 LiveData
的观察者处于激活状态的数量和新的状态判断是否调用 onActive
和 onInactive
,这两个也是 LiveData
的重要回调,不过都好理解,就不在细说了。最后会判断如今是不是激活状态,是的话就调用 LiveData
的 dispatchingValue
方法,顾名思义就是 LiveData
向其观察者发送通知。而 dispatchingValue
接收一个 ObserverWrapper
的参数,若是不为空就是说只用通知这个特定的观察者,不然通知全部处于激活的观察者。
setValue
和 postValue
setValue
直接改变当前的值,而后调用 dispatchingValue(null)
来通知全部激活状态的观察者,不过必须在主线程调用不然会抛异常。由于其余线程也均可以直接改变当前值的话会形成并发,加锁的话又会影响性能。
因此就又搞了一个 postValue
,它首先在拿到同步锁的状况下把值存到 mPendingData
,而后向主线程的 Handler
抛一个更新当前值的 mPostValueRunnable
,这个 mPostValueRunnable
在执行时也是先拿同步锁,而后调用 setValue
(如今在主线程)把 mPendingData
设置到当前值。在 mPostValueRunnable
抛出去以后且还未执行前,若是再次调用 postValue
就又会修改 mPendingData
的值,而不会再次向 Handler
抛一次 mPostValueRunnable
,这样就致使了后设置的值覆盖掉前面设置的,最后只会向观察者们通知最新的值。这个是须要注意的点,谷歌可能认为既然只是在主线程更新 View,那你拿最新的值就行,其余的都无所谓,固然这样也起到流量控制的做用,防止短期内过多的事件触发无用的回调。
谷歌推出的 AAC 库很好的解决了平常使用中的生命周期问题,使咱们能够专心于业务层面的设计,而不须要再为生命周期等问题担心。LiveData
和 ViewModel
确实好用,但用的时候也有须要注意的地方,ViewModel 层实际应用中要上承 View 层,提供必要的动做和数据,下接 Model 层,作好数据处理,都有不少须要考虑的地方,以后有时间再谈谈我在使用 AAC 的路上的经验和总结。
博客的 Github 仓库 欢迎你们 Start & Fork !