LiveData

LiveData是用来持有数据方便观测的,类似一个Observable。与一般的Observable不一样的地方在于,LiveData考虑到了app组件的生命周期。

基本使用

  1. 自定义类继承LiveData,指定泛型为所包含的数据类型
  2. 在类中更新数据的方法中调用setValue(T t)来通知绑定的观察者。

一个最简单的实现:
数据类:

public class User {

    private int id;
    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "id = " + id + ", name = " + name;
    }
}

自定义的LiveData:

public class LiveDataUser extends LiveData<User> {

    private User user;

    public LiveDataUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
        setValue(user);
    }
}

Activity或者Fragment中使用:

mLiveUser = new LiveDataUser(new User(15, "Tim"));
mLiveUser.observe(UserFragment.this, new Observer<User>() {
    @Override
    public void onChanged(@Nullable User user) {
        if (user != null) {
            mText.setText(user.toString());
        }
    }
});

当Observer的生命周期处于STARTED或者RESUMED的情况下,LiveData才认为这个Observer是活动的,LiveData只会通知活动状态下的Observer。

除了setValue(),LiveData还有两个重要的方法:
1. onActive():当LiveData中活动的Observer个数从0变成1的时候会被调用。
2. onInactive():当LiveData活动的Observer个数从1变成0的时候会被调用。注意此时仍然可能有Observer,只不过不是STARTED或者RESUMED状态,如果要检测是否有Observer,可以调用hasObservers()。onInactive()这个回调可以用来释放资源。

LiveData的observe()方法接收一个LifeCycleOwner
作为第一个参数,也就与生命周期相绑定了,达到以下效果:

  • 如果生命周期不在活动状态(也就是说不是STARTED或者RESUMED),那么即使观测值改变了,观测者也不会被调用。
  • 如果生命周期是destroyed的,那观测者会被自动移除掉。

因为LiveData是考虑了生命周期的,所以可以在Activity、Fragment之间共用,为了方便调用可以使用单例模式来创建LiveData:

public class LiveDataUser extends LiveData<User> {
    private static LiveDataUser sInstance = new LiveDataUser();
    private User user;

    private LiveDataUser() {
        user = new User(20, "小明");
    }

    public static LiveDataUser getInstance() {
        return sInstance;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
        setValue(user);
    }

    @Override
    protected void onActive() {
        super.onActive();
    }

    @Override
    protected void onInactive() {
        super.onInactive();
    }
}

一个LiveData有多个Observer的话,任意一个Observer在进入STARTED或者RESUMED状态后都可以收到最新的数据,比如在以下两个界面间切换:

public class MainActivity extends LifecycleActivity {

    private LiveDataUser mLiveUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final TextView text = (TextView) findViewById(R.id.text);

        mLiveUser = LiveDataUser.getInstance();
        mLiveUser.observe(MainActivity.this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                if (user != null) {
                    text.setText(user.toString());
                }
            }
        });
    }

    public void change(View view) {
        if (mLiveUser != null) {
            mLiveUser.setUser(new User(mLiveUser.getUser().getId() + 1, "Shin"));
        }
    }

    public void forward(View view) {
        startActivity(new Intent(this,SecondActivity.class));
    }
}


public class SecondActivity extends LifecycleActivity {

    private LiveDataUser mLiveUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        final TextView text = (TextView) findViewById(R.id.text);

        mLiveUser = LiveDataUser.getInstance();
        mLiveUser.observe(SecondActivity.this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                if (user != null) {
                    text.setText(user.toString());
                }
            }
        });
    }

    public void change(View view) {
        if (mLiveUser != null) {
            mLiveUser.setUser(new User(mLiveUser.getUser().getId() + 1, "Shin"));
        }
    }
}

LiveData的优点:
1. 没有内存泄露:因为观测者都与他们自己的生命周期绑定了,如果他们自己的生命周期destroyed掉了,观测者本身也就被清理掉了。
2. 不会因为已经停止的Activities闪退:如果某个观测者的生命周期是非活动的,比如说在后台,那就不会接收到回调。
3. 实时更新数据:如果某个观测者的生命周期又开始了,比如从后台回到前台,会接收到最近的数据。
4. 恰当的配置改变:如果因为配置改变导致Activity或者Fragment重建了,马上就可以收到之前可用的数据。
5. 资源分享:只要用一个监听实例,比如只要连接到系统服务一次,就可以在整个app当中注册多个observer。
6. 不必手动控制生命周期:如上例所示,Fragment中只要关心观测到的操作,不必关心生命周期变化,LiveData已经帮我们都完成了。

LiveData变换

如果想要在通知observer之前对LiveData的值进行变换,或者返回不同的LiveData,可以使用Transformations类。

  • Transformations.map():给LiveData所发送的值应用一个函数进行变换。返回一个新的LiveData,会发送变换后的值。该变换进行在主线程中。比如:
mLiveUser = LiveDataUser.getInstance();

final LiveData<String> transformedLiveUser = Transformations.map(mLiveUser, new Function<User, String>() {
    @Override
    public String apply(User input) {
        return "transformed " + input.getId();
    }
});

transformedLiveUser.observe(SecondActivity.this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String str) {
        if (str != null) {
            text.setText(str);
        }
    }
});

我们可以对初始LiveData进行变换,然后观测变换后的新的LiveData,这样初始LiveData的变化都会传递到我们的观测回调中。

  • Transformations.switchMap():创建一个新的LiveData,比如叫swLiveData,swLiveData会接收初始LiveData的变化,然后对于初始LiveData变化后的新值进行函数变换,swLiveData会转发新生成的LiveData所发送的值。函数变换在主线程中进行。
private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

使用这些变换允许observer的生命周期信息在调用链上传递,这样只有当observer观测到返回的LiveData的时候才会去计算这些变换。变换的这种延迟计算特性让我们可以直接去调用一些生命周期相关的操作不用考虑到生命周期的问题。

如果在ViewModel之中需要一个生命周期对象,变换可能就是解决方案。
比如说我们需要用户输入地址,然后返回地址对应的邮编给用户,一种错误的代码如下:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
       // DON'T DO THIS
       return repository.getPostCode(address);
    }
}

如果按照上面的实现来讲,UI每次调用getPosttalCode()都需要注销之前的LiveData,然后重新注册这个新的LiveData实例。而且如果界面重建了,会再次调用repository.getPostCode()而不是使用之前的结果。

正确的做法应该是进行变换:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

因为postalCode这个LiveData是不会变的,所以我们把它声明成final的。如果当addressInput改变的时候,有活动的观测者的情况下才会调用repository.getPostCode(),如果没有的话就会等到有活动观测者的时候才会去调用。

因为这个延迟加载机制,所以可以定义一些基本的LiveData,ViewModel就可以轻松获取并进行转换得到想要的结果。

如果想要自定义变换,可以使用MediatorLiveData这个类,这个类可以用来监听其他LiveData并处理所发送的事件。MediatorLiveData会把它的活动或者非活动状态传递给源LiveData。可以查看Transformations了解具体实现。

MediatorLiveData

MediatorLiveData是LiveData的一个子类,可以观测一个或多个同种数据类型LiveData的变化,每次所观测LiveData中的某一个发生变化时,都可以在onChanged()中接收到回调,添加观测对象的方法为addSource(LiveData, Observer)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值