美文网首页Android开发Android开发经验谈Android技术知识
Android之MVVM架构指南(四):LiveData

Android之MVVM架构指南(四):LiveData

作者: 吴七禁 | 来源:发表于2018-10-22 16:30 被阅读12次

    Livedata 是一个数据源的包装类,他可以有效的取代请求信息时用到callback接口,还可以配合Lifecycle感知程序组件生命周期。

    正常我们请求网络数据时的代码为:

    public class ListActivity extends AppCompatActivity {
        private TextView userNameTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            userNameTv = findViewbyId(R.id.user_name);
            NetModel.getUserName().callback(new Callback(){
    
                @Override
                public void onSuccess(String userName){
                    userNameTv.setText(userName);
                }
    
            });
        }
    }
    

    同样的功能使用LiveData可以写为:

    public class ListActivity extends AppCompatActivity {
    
        private TextView userNameTv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            userNameTv = findViewbyId(R.id.user_name);
            LiveData<String> userName = NetModel.getUserName();
            userName.observe(this, new Observer<String>() {
                @Override
                public void onChanged(@Nullable String name) {
                   userNameTv.setText(name);
                }
            });
        }
    }
    
    

    使用时看上去并没有简化多少,但是相比之前的原始代码,如果原始代码中不对setText()方法添加生命周期的判断,当activity销毁后可能会引发空指针异常。

    而在LiveData的observe方法中需要传入一个LifecycleOwner对象,所以LiveData可以感知宿主的生命周期,从而不用担心此类问题。

    创建Livedata对象

    Livedata 属于包装类,所以需要先创建一个Livedata对象,然后在为其填充数据。

    Livedata 是一个一个抽象类,不能直接实例化,Android 默认提供了一个它的子类MutableLiveData,其源码如下:

    public class MutableLiveData<T> extends LiveData<T> {
        @Override
        public void postValue(T value) {
            super.postValue(value);
        }
    
        @Override
        public void setValue(T value) {
            super.setValue(value);
        }
    }
    

    有没有一脸懵逼的感觉?其实这么写的原因是因为Livedata类添加数据的setValue()postValue()方法的权限不是公开的,而MutableLiveData只是将这两个方法权限公开而已。

    为什么要这样做呢?主要是考虑到架构模型的问题,在数据源请求处使用MutableLiveData对象添加数据,而在UI操作的地方使用LiveData对象就只能使用数据无法改变其数据,这样的话就做到了数据只能在一个地方发生改变提升系统稳定性。当然如果你希望在任何地方都可以改变数据源,直接全部使用MutableLiveData对象即可。

    至于设置数据的两个方法:

    • setValue(),在UI线程设置数据。
    • postValue(),在worker线程设置数据。

    具体使用方法:

    final MutableLiveData<String> data = new MutableLiveData<>();
    data.setValue("test");
    
    // in Worker Thread used
    data.postValue("test");
    
    

    感知生命周期

    当我们调用LiveData的observe(LifecycleOwner owner,Observer<T> observer)时,LiveData会在LifecycleOwner中添加一个监听生命周期的观察者,

    • 当生命周期处于STARTEDRESUMED这种活动状态时才会通知Observer数据更新
    • 当生命周期处于非活动状态Observer不会接收到数据更新的通知
    • 当生命周期处于DESTROYED时会将监听生命周期的观察者删除

    扩展使用

    在上面生命周期的三种情况中,LiveData 除了内部的逻辑操作外,还提供了跟生命周期状态有关的两个回调方法:

    • onActive() ,当生命周期观察者处于活动状态后调用。

    • onInactive(),当生命周期观察者处于非活动状态后调用。

    基于此,我们可以继承 LiveData进行扩展实现定制化,下面举一个定位的例子:

    public class LocationLiveData extends LiveData<Position> {
    
          // 定位管理器
        private LocationManager mLocationManager;
    
        public LocationLiveData() {
            mLocationManager = new LocationManager(new LocationCallback() {
                @Override
                public void onUpdate(Position position) {
                       // 当位置信息更新时重置livedata数据
                    setValue(position);
                }
            });
        }
    
        @Override
        protected void onActive() {
              // 生命周期处于活动状态时开启定位
            mLocationManager.startLocation();
        }
    
        @Override
        protected void onInactive() {
               //生命周期处于非活动状态时关闭定位
            mLocationManager.stopLocation();
        }
    }
    

    上面的代码将定位功能封装到了Livedata中,隐藏细节的同时并具备了感知生命周期的能力,在实际使用中只需要像标准的LiveData使用一样即可:

    public class LocationFragment extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            
            LiveData<Position> position = new LocationLiveData();
            position.observe(this,new Observer(){
                @Override
                public void onChanged(Position position) {
                    // 更新UI
                }
            });
        }
    }
    

    不仅如此,我们知道 LiveData 中的observe()方法需要传入一个LifecycleOwner对象用来感知生命周期,但是它并不是唯一值,也就是说我们能够通过observe()方法同时为一个liveData对象设置两个Activity/Fragment的生命周期宿主,也就是LifecycleOwner,基于这个特性,我们可以将LiveData设置成单例模式从而实现多个组件间共享数据。

    public class LocationLiveData extends LiveData<Position> {
        private LocationManager mLocationManager;
    
        private LocationLiveData instance;
    
        public static LocationLiveData get(){
            if (instance == null) {
                instance = new LocationLiveData();
            }
            return instance;
        }
    
    
        private LocationLiveData() {
            mLocationManager = new LocationManager(new LocationCallback() {
                @Override
                public void onUpdate(Position position) {
                    setValue(position);
                }
            });
        }
    
        @Override
        protected void onActive() {
            mLocationManager.startLocation();
        }
    
        @Override
        protected void onInactive() {
            mLocationManager.stopLocation();
        }
    }
    

    在不同的组件中调用:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            
            LocationLiveData.get().observe(this,new Observer(){
                @Override
                public void onChanged(Position position) {
                    // 更新UI
                }
            });
        }
    }
    
    
    public class Fragment2 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            
            LocationLiveData.get().observe(this,new Observer(){
                @Override
                public void onChanged(Position position) {
                    // 更新UI
                }
            });
        }
    }
    

    变换

    这个功能跟RxJava的变换很像,说白了就是抄的RxJava的功能,实现变换功能的是Transformations类,它提供了两个变化的方法。

    map() 方法

    假设我们要实现一个获取所有用户的用户名功能,但是我们只有获取所有用户的接口:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
           
            NetModel.getAllUser().observe(this, new Observer<List<user>>() {
                @Override
                public void onChanged(@Nullable List<User> users) {
                    // 获取到所有用户在去拿用户名
                    List<String> allUserName = new ArrayList<>();
                    for (User user : users) {
                        allUserName.add(user.getUserName());
                    }
                    // 更新UI
                }
            });
        }
    }
    
    

    使用 map() 方法效果如下:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            
            LiveData<List<String>> data = Transformations.map(NetModel.getAllUser(), new Function<List<User>, List<String>>() {
                @Override
                public List<String> apply(List<User> input) {
                    List<String> allUserName = new ArrayList<>();
                    for (User user : users) {
                        allUserName.add(user.getUserName());
                    }
                    return allUserName;
                }
            });
    
            data.observe(this, new Observer<List<String>>() {
                @Override
                public void onChanged(@Nullable List<String> strings) {
                    // 更新UI
                }
            });
    
    
        }
    }
    

    这样做的好处除了转变操作也能感知生命周期外,更重要的是业务逻辑分离,onChanged()方法中只做更新UI的操作,代码更加健壮。

    switchMap() 方法

    这两种方法唯一不同的地方就是map()的变换是从一个数据源变成另外一个数据源,而switchMap()是从一个数据源变成另外一个LiveData对象,具体有什么用呢?

    举一个例子,我们想实现一个获取用户所有信息的功能,但是我们需要先获取用户的id,才能通过id获取信息:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            NetModel.getUserId().observe(this,new Observer<String>(){
                @Override
                public void onChanged(String id){
                    NetModel.getUserInfo(id).observe(Fragment1.this,new Observer<User>(){
                        @Override
                        public void onChanged(User user){
                            // 更新UI
                        }
                    })
                }
           });
        }
    }
    
    

    很明显,看着就不爽,用switchMap() 实现效果如下:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            LiveData<User> user = Transformations.switchMap(NetModel.getUserId(), new Function<String, LiveData<User>() {
                @Override
                public LiveData<User> apply(String id) {
                    return Model.getUserInfo(id);
                }
            });
    
            user.observe(this, new Observer<User>() {
                @Override
                public void onChanged(@Nullable List<User> users) {
                    // 更新UI
                }
            });
    
        }
    }
    

    同样的道理,这么做不光方便管理,代码更加健壮。

    合并多个LiveData

    假设我们有一个获取用户信息的功能,用户信息既可以在网络获取也可以在本地缓存中获取,需要怎么实现呢?

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
           
            LocalModel.getUserInfo().observe(this, new Observer<User>() {
                @Override
                public void onChanged(@Nullable List<User> users) {
                    // 更新UI
                }
            });
    
            NetModel.getUserInfo().observe(this, new Observer<User>() {
                @Override
                public void onChanged(@Nullable List<User> users) {
                    // 更新UI
                }
            });
    
        }
    }
    

    上面代码有一个严重的缺陷,那就是两个observer中更新UI代码其实是一样的,这是冗余代码很垃圾,那应该怎么做呢?Android提供了一个可以将多个LiveData 对象合并成一个Livedata的功能类:MediatorLiveData,使用方法如下:

    public class Fragment1 extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
           
            MediatorLiveData<User> data = new MediatorLiveData();
            data.addSource(LocalModel.getUserInfo(), new Observer<User>() {
                @Override
                public void onChanged(@Nullable User user) {
                    data.setValue(user);
                }
            });
            data.addSource(NetModel.getUserInfo(), new Observer<User>() {
                @Override
                public void onChanged(@Nullable User user) {
                    data.setValue(user);
                }
            });
            data.observe(this,new Observer<User>() {
                @Override
                public void onChanged(@Nullable User user) {
                    // 更新UI
                }
            });
    
        }
    }
    

    MediatorLiveData 通过addSource()方法可以观察多个Livedata对象的数据变化。

    下一节

    Android之MVVM架构指南(五):ViewModel

    相关文章

      网友评论

        本文标题:Android之MVVM架构指南(四):LiveData

        本文链接:https://www.haomeiwen.com/subject/ezpgzftx.html