LiveData overview
LiveData
持有的数据与 activities, fragments, or services 等的生命周期同步。
Activity 或者 Fragment 实现 LifecycleOwner
接口,在继承的方法中进行生命周期管理。在最新的SDK中,AppCompatActivit 和 Fragment 已经实现了 LifecycleOwner 接口,并进行了生命周期的管理。
优点:
1. 确保 UI 与数据状态相匹配。
当订阅者的数据发生改变时,UI便发生改变。
2. 不会发生内存泄漏
当 Lifecycle
对象被销毁时,会取消订阅者和 Lifecycle
对象的关联。
3. 不需要再去处理生命周期中的数据
4. 始终保持最新数据
5. 正确处理数据
即使是在 Activity 或者 Fragment 的 reOncreate 后,也会接收最新的有效数据
6. 共享数据
如:继承 LiveData
使用:
- 通常在
ViewModel
中,创建LiveData
实例持有数据。 - 在观察者对象中,创建 onChanged()方法。当数据放生变化时,调用
onChanged()
方法。通常在 Activity 和 Fragment 中,创建观察者对象。 - 使用
observe()
方法,关联观察者对象和 LiveData 对象。调用observe()
方法需要一个 LifecycleOwner 对象,所以通常在 Activity 或者 Fragment 中关联观察者对象。 - LiveDada 是任何实现了集合(Collections)接口对象的包装。
示例:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData mCurrentName;
public MutableLiveData getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData();
}
return mCurrentName;
}
// Rest of the ViewModel...
}
public class NameActivity extends AppCompatActivity {
private NameViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
mModel = ViewModelProviders.of(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer nameObserver = new Observer() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
mNameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
mModel.getCurrentName().observe(this, nameObserver);
...
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
mModel.getCurrentName().setValue(anotherName);// mNameTextView 显示的内容发生改变
}
});
}
}
5.结合 Room
使用。在 Room
章节中进行介绍.
6.继承 LiveData
public class StockLiveData extends LiveData {
private StockManager mStockManager;
private SimplePriceListener mListener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
mStockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
mStockManager.requestPriceUpdates(mListener);
}
@Override
protected void onInactive() {
mStockManager.removeUpdates(mListener);
}
}
此例子中有以下几个重要方法:
-
onActive()
当 LiveData对象有一个活跃的观察者时,此方法调用。 -
onInactive()
StockManager
与 LiveData 不在有联系 - The
setValue(T)
当数据改变时,刷新UI
使用:
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LiveData myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
LiveData 使用单例模式(Kotlin 中单例在伴生对象中设置):
public class StockLiveData extends LiveData {
private static StockLiveData sInstance;
private StockManager mStockManager;
...
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
...
}
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(getActivity()).observe(this, price -> {
// Update the UI.
});
}
}
数据转换(Transform LiveData)
使用Transformations
类
Transformations.map()
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
private LiveData getUser(String id) {
...;
}
LiveData userId = ...;
LiveData user = Transformations.switchMap(userId, id -> getUser(id) );
/*
*错误示例
*UI组件需要从先前的LiveData对象取消注册,并在每次调用getPostalCode()时注册新实例.此外,如果重新创建UI组件,它将*触发另一个对repository.getPostCode()方法的调用,而不是使用前一个调用的结果。
*/
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData getPostalCode(String address) {
// DON'T DO THIS
return repository.getPostCode(address);
}
}
//正确示例
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData addressInput = new MutableLiveData();
public final LiveData 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 属性是 final 修饰的。如果需要可变的LiveData,可以使用 MediatorLiveData
。它是 LiveData的子类。
当从网络或者数据库中加载数据,更新 UI界面时,则使用MediatorLiveData
例:
//a generic class that describes a data with a status
public class Resource {
@NonNull public final Status status;
@Nullable public final T data;
@Nullable public final String message;
private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) {
this.status = status;
this.data = data;
this.message = message;
}
public static Resource success(@NonNull T data) {
return new Resource<>(SUCCESS, data, null);
}
public static Resource error(String msg, @Nullable T data) {
return new Resource<>(ERROR, data, msg);
}
public static Resource loading(@Nullable T data) {
return new Resource<>(LOADING, data, null);
}
}
// ResultType: Type for the Resource data
// RequestType: Type for the API response
public abstract class NetworkBoundResource {
// Called to save the result of the API response into the database
@WorkerThread
protected abstract void saveCallResult(@NonNull RequestType item);
// Called with the data in the database to decide whether it should be
// fetched from the network.
@MainThread
protected abstract boolean shouldFetch(@Nullable ResultType data);
// Called to get the cached data from the database
@NonNull @MainThread
protected abstract LiveData loadFromDb();
// Called to create the API call.
@NonNull @MainThread
protected abstract LiveData> createCall();
// Called when the fetch fails. The child class may want to reset components
// like rate limiter.
@MainThread
protected void onFetchFailed() {
}
// returns a LiveData that represents the resource, implemented
// in the base class.
public final LiveData> getAsLiveData();
}
public abstract class NetworkBoundResource {
private final MediatorLiveData> result = new MediatorLiveData<>();
@MainThread
NetworkBoundResource() {
result.setValue(Resource.loading(null));
LiveData dbSource = loadFromDb();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldFetch(data)) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource,
newData -> result.setValue(Resource.success(newData)));
}
});
}
private void fetchFromNetwork(final LiveData dbSource) {
LiveData> apiResponse = createCall();
// we re-attach dbSource as a new source,
// it will dispatch its latest value quickly
result.addSource(dbSource,
newData -> result.setValue(Resource.loading(newData)));
result.addSource(apiResponse, response -> {
result.removeSource(apiResponse);
result.removeSource(dbSource);
//noinspection ConstantConditions
if (response.isSuccessful()) {
saveResultAndReInit(response);
} else {
onFetchFailed();
result.addSource(dbSource,
newData -> result.setValue(
Resource.error(response.errorMessage, newData)));
}
});
}
@MainThread
private void saveResultAndReInit(ApiResponse response) {
new AsyncTask() {
@Override
protected Void doInBackground(Void... voids) {
saveCallResult(response.body);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb(),
newData -> result.setValue(Resource.success(newData)));
}
}.execute();
}
public final LiveData> getAsLiveData() {
return result;
}
}
class UserRepository {
Webservice webservice;
UserDao userDao;
public LiveData> loadUser(final String userId) {
return new NetworkBoundResource() {
@Override
protected void saveCallResult(@NonNull User item) {
userDao.insert(item);
}
@Override
protected boolean shouldFetch(@Nullable User data) {
return rateLimiter.canFetch(userId) && (data == null || !isFresh(data));
}
@NonNull @Override
protected LiveData loadFromDb() {
return userDao.load(userId);
}
@NonNull @Override
protected LiveData> createCall() {
return webservice.getUser(userId);
}
}.getAsLiveData();
}
}