Android Architecture Components系列之LiveData&ViewModel

上一篇咱们聊到了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数据持久化方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值