上一篇咱们聊到了Android Architecture Components系列之Lifecicle,优雅解决了Activity生命周期方法的分离。不过也仅此而已,它并不是一种app架构,而我们今天的主角LiveData和ViewModel则是一种官方的、稳定的app架构方案,算是google给我们广大开发者的馈赠。
LiveData
LiveData是一个数据容器,专门用来存放数据的,不同的是它基于观察者模式。LiveData在这里既充当了被观察者的角色,又充当了观察者的角色。它所持有的数据发生任何变化,前台组件(Activity、Fragment等)都可以感知到,并及时对ui进行更新,此为被观察者的角色。同时它还可以持有组件的生命周期状态Lifecycle,意味着它会在界面代码(LifecycleOwner)的生命周期处于 Started 或 Resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新,因此避免了内存泄露的产生,此为观察者的角色。如果对Lifecycle组件还不熟悉的,可以去看一看上一篇博客Lifecycle。
总结一下:LiveData不会产生内存泄漏,可以通知前台组件数据的变化。
ViewModel
我们知道,传统的mvc模式,Activity或Fragment等前台组件,不仅充当了ui层,也充当了数据层,没有很好的分离,导致在业务逻辑很复杂的时候,Activity中的代码会变的异常臃肿,难以维护,以此衍生出来的诸如MVP、MVVM等app架构方案去实现app的ui和数据的分离。这里不去讨论这两种架构方案,我们的重点是ViewModel,重点是官方-。-
ViewModel是将前台页面的数据处理逻辑(比如联网获取数据、从数据库读数据)等代码分离出来。这样前台组件就能注重页面的显示,而数据处理的逻辑都交给ViewModel,然后ViewModel再交给LiveData,LiveData再去通知前台组件更新ui,大体就这么个流程。
不同的是,ViewModel中存储的数据只有当Activtiy或Fragment全部销毁时才会消失,就比如我们在旋转屏幕的时候,常常会通过onSaveInstanceState去保存数据,然后再onCreate中去恢复数据,但这个方案都知道有一定的局限性,比如存储的数据不能太大,写起来也比较麻烦。而使用ViewModel则完全不需要考虑这个问题,ViewModel中存储的数据不会随着诸如屏幕旋转等事件而消失。而且它是可以共享的,可以在Activity和Fragment中共享这些数据。总的来说,它是暂时永恒且共享的,可以省去我们不少麻烦。
注:LiveData和ViewModel配合使用才是最正确的打开方式。
好吧,扯完了理论,我们来个例子使用一下,就可以豁然开朗了。
例:我想要在MainActivity中动态请求一组数据。
往常的做法,我们肯定是在MainActivity中用网络框架去请求数据,请求成功后去更新界面,在正常不过的逻辑了。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
//设置baseUrl,baseUrl+接口中配置的地址组成真正的请求地址。
.baseUrl("http://wanandroid.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
.client(new OkHttpClient())
.build();
WanAndroidService service = retrofit.create(WanAndroidService.class);
service.getPublicAccountList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<PublicAccountBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(PublicAccountBean publicAccountBean) {
//请求成功
for (int i = 0; i < publicAccountBean.getData().size(); i++) {
Log.e("data----------", publicAccountBean.getData().get(i).getName());
}
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
网络请求用的Retrofit和RxJava,请求地址是鸿洋的玩Android的api。 WanAndroidService代码如下:
public interface WanAndroidService {
/**
* 获取公众号列表
* @return SessionBean
*/
@GET("wxarticle/chapters/json")
Observable<PublicAccountBean> getPublicAccountList();
}
PublicAccountBean:
public class PublicAccountBean {
private int errorCode;
private String errorMsg;
private List<DataBean> data;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
public static class DataBean {
private String courseId;
private String id;
private String name;
private String order;
private String parentChapterId;
private boolean userControlSetTop;
private String visible;
private List<?> children;
public String getCourseId() {
return courseId;
}
public void setCourseId(String courseId) {
this.courseId = courseId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
public String getParentChapterId() {
return parentChapterId;
}
public void setParentChapterId(String parentChapterId) {
this.parentChapterId = parentChapterId;
}
public boolean isUserControlSetTop() {
return userControlSetTop;
}
public void setUserControlSetTop(boolean userControlSetTop) {
this.userControlSetTop = userControlSetTop;
}
public String getVisible() {
return visible;
}
public void setVisible(String visible) {
this.visible = visible;
}
public List<?> getChildren() {
return children;
}
public void setChildren(List<?> children) {
this.children = children;
}
}
@Override
public String toString() {
return "PublicAccountBean{" +
"errorCode=" + errorCode +
", errorMsg='" + errorMsg + '\'' +
", data=" + data +
'}';
}
}
数据妥妥的有-。-
事实证明我们的逻辑没有任何问题,完美的无可挑剔~
现在我们运用ViewMode和LiveData配合,把数据请求的逻辑分离开。走你~
新建MainViewModel类,继承自ViewModel,使用之前要先引入依赖:
api "android.arch.lifecycle:extensions:1.1.1"
MainViewModel:
public class MainViewModel extends ViewModel {
private MutableLiveData<PublicAccountBean> accountData;
public LiveData<PublicAccountBean> getAccountData() {
if (accountData == null) {
accountData = new MutableLiveData<>();
loadData();
}
return accountData;
}
private void loadData() {
Retrofit retrofit = new Retrofit.Builder()
//设置baseUrl,baseUrl+接口中配置的地址组成真正的请求地址。
.baseUrl("http://wanandroid.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
.client(new OkHttpClient())
.build();
WanAndroidService service = retrofit.create(WanAndroidService.class);
service.getPublicAccountList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<PublicAccountBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(PublicAccountBean publicAccountBean) {
//请求成功
accountData.setValue(publicAccountBean);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
LiveData是抽象类,这里用LiveData的子类MutableLiveData
MainActivity代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainViewModel model = ViewModelProviders.of(this).get(MainViewModel.class);
model.getAccountData().observe(this, new android.arch.lifecycle.Observer<PublicAccountBean>() {
@Override
public void onChanged(@Nullable PublicAccountBean publicAccountBean) {
for (int i = 0; i < publicAccountBean.getData().size(); i++) {
Log.e("data----------", publicAccountBean.getData().get(i).getName());
}
}
});
}
}
结果嘛,依旧有:
让我们来分析一下:MainViewModel是一个数据处理类,负责数据的请求和处理。请求成功后把数据放到LiveData数据容器中,在MainActivity中,我们通过ViewModelProviders类的静态方法获取到MainViewModel对象,并获取到ViewModel中的数据容器对象,为这个数据容器对象添加观察者(MainActivity),数据请求成功后通知MainActivtiy更新数据,流程其实很简单,一点也不复杂。我们来看下源码,首先看下ViewModel的observe方法:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
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);
}
可以看到LifecycleOwner(Activity、Fragment等拥有生命周期的组件),如果组件的生命周期是onDestroy,则直接忽略,不再进行数据的观察和推送。而对组件生命周期和数据的观察则是通过LifecycleBoundObserver类来进行的:
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<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);
}
}
可以看到当组件的生命周期发生变化,会回调onStateChanged方法,如果组件的生命周期结束,则移除该观察者,不再监听数据的变化。否则调用activeStateChanged方法:
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) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
这里重点看下dispatchingValue方法,其中会调用一个叫considerNotify的方法,这个方法实现了对数据的观察者模式,使得数据发生变化时,可以通知前台进行数据更新。
大体的流程就是这样,有兴趣的可以自己翻一翻源码,除了这个我们刚刚还提到了共享,通过ViewModel提供的数据是可以在不同的组件之间进行共享的,比如MainActivity代码不变,只是在activity中添加了一个Fragment,然后在Fragment中监听ViewModel中数据的变化,然后弹出一个Toast。
MainActivity:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//添加一个Fragment。
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frame, new MyFrgment())
.commit();
MainViewModel model = ViewModelProviders.of(this).get(MainViewModel.class);
model.getAccountData().observe(this, new android.arch.lifecycle.Observer<PublicAccountBean>() {
@Override
public void onChanged(@Nullable PublicAccountBean publicAccountBean) {
for (int i = 0; i < publicAccountBean.getData().size(); i++) {
Log.e("data----------", publicAccountBean.getData().get(i).getName());
}
}
});
}
}
MyFragment:
public class MyFrgment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_my, container, false);
MainViewModel model = ViewModelProviders.of(this).get(MainViewModel.class);
model.getAccountData().observe(this, new android.arch.lifecycle.Observer<PublicAccountBean>() {
@Override
public void onChanged(@Nullable PublicAccountBean publicAccountBean) {
Toast.makeText(getActivity(),publicAccountBean.toString(),Toast.LENGTH_LONG).show();
}
});
return root;
}
}
R.layout.fragment_my是个空的布局,只不过加一个黄色的背景,运行后可以发现,除了打印出数据,还会以Toast的形式弹出数据,说明各组件之间是可以共享数据的。
到这里,LiveData和ViewModel的基本使用已经讲完了,其实这里负责请求数据的是ViewModel,负责数据处理的也是ViewModel,ViewModel承担了太多职责,因此可以把数据请求的逻辑再进一步剥离。使得ViewModel的代码更加简洁,把这个专门负责请求数据的类称为XXRepository。举个例子,我们把刚刚获取数据的逻辑放在另外一个类AccountRepository中。
public class AccountRepository {
public MutableLiveData<PublicAccountBean> getAccountData() {
final MutableLiveData<PublicAccountBean> accountData = new MutableLiveData<>();
Retrofit retrofit = new Retrofit.Builder()
//设置baseUrl,baseUrl+接口中配置的地址组成真正的请求地址。
.baseUrl("http://wanandroid.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
.client(new OkHttpClient())
.build();
WanAndroidService service = retrofit.create(WanAndroidService.class);
service.getPublicAccountList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<PublicAccountBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(PublicAccountBean publicAccountBean) {
//请求成功
accountData.setValue(publicAccountBean);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
return accountData;
}
}
ViewModel中的代码稍作修改,通过AccountRepository 去获取数据:
public class MainViewModel extends ViewModel {
AccountRepository accountRepository = new AccountRepository();
public LiveData<PublicAccountBean> getAccountData() {
return accountRepository.getAccountData();
}
}
效果是一样的,这里就不贴了,这样又减轻了ViewModel的负担,更加解耦,不过使得类的数量又增加了,具体该如何使用,还需要自己斟酌。附上几点总结:
1. ViewMode和LiveData通常配合使用,ViewModel负责提供LivaData,LiveData负责存储数据。
2. 开发者在开发时无需关心生命周期所带来的各种问题。
3. 组件之间可以共享数据。
4. LiveData基于观察者模式,可以将数据的变化及时通知给前台组件。
5. ViewModel可以进一步拆分,更加职责分离。
完~
下一篇:Android Architecture Components系列之Room数据持久化方案