Android Jetpack - LiveData

Android Jetpack - LiveData_第1张图片

LiveData 基本操作

LiveData 一般是和 ViewModel 结合起来使用的,比如计数器功能,在单线程模式下确实可以正常工作,但如果在 ViewModel 的内部开启了线程去执行一些耗时逻辑,那么在点击按钮后就我们想立即去获取最新的数据,但是得到的肯定还是之前的旧数据。

**原本我们一直使用的都是在 Activity 中手动的获取 ViewModel 中的数据这种交互方式,但是 ViewModel 却无法将数据的变化主动地通知给 Activity。**那么怎么实现主动通知呢?或许你会说将 Activity 的实例传给 ViewModel,这样 ViewModel 不就能主动通知 Activity 了吗?很可惜,这是错误的。因为 ViewModel 的生命周期是长于 Activity,如果这样做了,那么可能因为 Activity 无法释放而造成内存泄漏。

LiveData 可以轻松地解决这个问题。LiveData 可以包含任何类型的数据,并在数据发生变化的时候通知给观察者。也就是说,我们可以将数据使用 LiveData 来包装,然后在 Activity 中去观察它,就可以将数据变化通知给 Activity。

使用示例:这里和 ViewModel 结合使用

  1. 创建 ViewModel 将里面的数据用 LiveData 包装起来
public class MyViewModel extends ViewModel {

    public MyViewModel(int countReserved) {
        this._counter.setValue(countReserved);
    }
    // LiveData 是不可变的
    // 我们给 _counter 加上 private 修饰符,这样 _counter 外部就不可见了。
    // 然后我们定义不可变的 counter 变量,并让它的 getValue() 方法返回 _counter 中的数据。
    // 这样,当外部调用 counter 变量时,其实获取到的是 _counter 的值,
    // 但是无法给 _counter 设置数据,从而保证了ViewModel 的数据封装性 
    public LiveData<Integer> counter = new LiveData<Integer>() {
        @Override
        public Integer getValue() {
            return _counter.getValue();
        }
    };
    // MutableLiveData 是一种可变的 LiveData
    private MutableLiveData<Integer> _counter = new MutableLiveData<Integer>();

    public void plus() {
        int value = counter.getValue() == null ? 0 : counter.getValue()+1;
        _counter.setValue(value);
    }

    public void clear() {
        _counter.setValue(0);
    }
}

主要有三种读写数据的方法,分别是 getValue()、setValue() 和 postValue() 方法。setValue() 只能在主线程中使用,postValue() 可以用于非主线程中使用

  1. 通过viewModel.counter 的 observe() 方法来观察数据的变化:
		viewModel1 = new ViewModelProvider(this).get(MyViewModel.class);
        // 当 counter 中包含的数据发生变化时就会回调到这里
        viewModel1.counter.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                String text = viewModel1.counter.getValue().toString();
                infoText.setText(text);
            }
        });
        ...      

map 和 switchMap

map() 方法的作用:将实际包含数据的 LiveData 和 仅用于观察数据的 LiveData 进行转换。

举个栗子:
比如有一个 User 类,User 中包含姓名和年龄,如果观察者只需要姓名而不关心年龄,那么这个时候还将整个 User 类型的 LiveData 暴露给外部就显得不那么合适了。

public class User {
    String firstname;
    String lastname;
    int age;

    public User(String firstname, String lastname, int age) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.age = age;
    }
}

public class MyViewModel extends ViewModel {
    public MyViewModel(User user) {
        this.userLiveData.setValue(user);
    }
  
    private final MutableLiveData<User> userLiveData = new MutableLiveData<>();
    // 调用 Transformations 的 map() 方法来对LiveData 的数据类型进行转换,
    // map接收两个参数,第一个参数是原始的 LiveData 对象;
    // 第二个参数是一个转换函数,我们在这里编写具体的转换逻辑即可。
    public final LiveData<String> username = Transformations.
            map(userLiveData, new Function<User, String>() {
                @Override
                public String apply(User user) {
                    return user.firstname+", "+user.lastname;
                }
            });
}

当 userLiveData 的数据发生变化时,map() 方法会监听到变化并执行转换函数中的逻辑,然后再将转换之后的数据通知给userLiveData 的观察者。

switchMap()

前面我们所学的所有内容都有一个前提:LiveData 对象的实例都是在 ViewModel 中创建的。然而在实际的项目中,不可能一直是这种理想情况,很有可能 ViewModel 中的某个 LiveData 对象是调用另外的方法获取的,那么我们就可以借助 switchMap() 方法将这个LiveData 对象转换成另一个可观察的 LiveData 对象。

举个栗子:
这里我们在 Repository 类中添加了一个 getUser() 方法,这个方法接收一个 userId 参数,每次将传入的 userId 当作用户姓名来创建一个新的 User 对象即可。

// 单例类
public class Repository {
    private static Repository repository;

    public static Repository getRepository() {
        if (repository == null) {
            repository = new Repository();
        }
        return repository;
    }

    public LiveData<User> getUser(String userId) {
        MutableLiveData<User> liveData = new MutableLiveData<>();
        liveData.setValue(new User(userId, userId, 0));
        return liveData;
    }
}
// 这里我们定义了一个新的 userIdLiveData 对象,用来观察 userId 的数据变化,
// 然后调用了 Transformations 的 switchMap() 方法,用来对另一个可观察的 LiveData 对象进行转换。
public class MyViewModel extends ViewModel {

    private final MutableLiveData<String> userIdLiveData =
            new MutableLiveData<>();

    public final LiveData<User> user = Transformations.switchMap(userIdLiveData,
            new Function<String, LiveData<User>>() {
                @Override
                public LiveData<User> apply(String userId) {
                    return Repository.getRepository().getUser(userId);
                }
            });

    public void getUser(String userId) {
        userIdLiveData.setValue(userId);
    }
}

我么再来梳理一遍它的整体工作流程:首先,当外部调用 MyViewModel 的 getUser() 方法来获取用户数据时,并不会发起任何请求和函数调用,只会将传入的 userId 值设置到 userIdLiveData 当中。一旦 userIdLiveData 的数据发生变化,那么观察 userIdLiveData 的 switchMap() 方法就会执行,并且调用我们编写的转换函数。然后在转换函数中调用 Repository.getUser() 方法获取真正的用户数据。同时 switchMap() 方法会将 Repository.getUser() 方法返回的 LiveData 对象转换成一个可观察的 LiveData 对象,对于 Activity 而言,只要去观察这个 LiveData 对象即可。

你可能感兴趣的:(Android,Jetpack,android,android,jetpack,kotlin)