LiveData

作者: falcon_li | 来源:发表于2018-08-20 15:24 被阅读0次

    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


    使用:

    1. 通常在 ViewModel 中,创建 LiveData 实例持有数据。
    2. 在观察者对象中,创建 onChanged()方法。当数据放生变化时,调用 onChanged()方法。通常在 Activity 和 Fragment 中,创建观察者对象。
    3. 使用 observe()方法,关联观察者对象和 LiveData 对象。调用 observe()方法需要一个 LifecycleOwner 对象,所以通常在 Activity 或者 Fragment 中关联观察者对象。
    4. LiveDada 是任何实现了集合(Collections)接口对象的包装。
      示例:
    public class NameViewModel extends ViewModel {
    
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;
    
        public MutableLiveData<String> getCurrentName() {
            if (mCurrentName == null) {
                mCurrentName = new MutableLiveData<String>();
            }
            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<String> nameObserver = new Observer<String>() {
                @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<BigDecimal> {
        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<BigDecimal> myPriceListener = ...;
            myPriceListener.observe(this, price -> {
                // Update the UI.
            });
        }
    }
    

    LiveData 使用单例模式(Kotlin 中单例在伴生对象中设置):

    public class StockLiveData extends LiveData<BigDecimal> {
        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<User> userLiveData = ...;
    LiveData<String> userName = Transformations.map(userLiveData, user -> {
       user.name + " " + user.lastName
    });
    

    Transformations.switchMap()

    private LiveData<User> getUser(String id) {
      ...;
    }
    
    LiveData<String> userId = ...;
    LiveData<User> 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<String> getPostalCode(String address) {
           // DON'T DO THIS
           return repository.getPostCode(address);
        }
    }
    //正确示例
    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 属性是 final 修饰的。如果需要可变的LiveData,可以使用 MediatorLiveData。它是 LiveData的子类。
    当从网络或者数据库中加载数据,更新 UI界面时,则使用MediatorLiveData
    例:

    //a generic class that describes a data with a status
    public class Resource<T> {
        @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 <T> Resource<T> success(@NonNull T data) {
            return new Resource<>(SUCCESS, data, null);
        }
    
        public static <T> Resource<T> error(String msg, @Nullable T data) {
            return new Resource<>(ERROR, data, msg);
        }
    
        public static <T> Resource<T> 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<ResultType, RequestType> {
        // 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<ResultType> loadFromDb();
    
        // Called to create the API call.
        @NonNull @MainThread
        protected abstract LiveData<ApiResponse<RequestType>> 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<Resource<ResultType>> getAsLiveData();
    }
    
    public abstract class NetworkBoundResource<ResultType, RequestType> {
        private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
    
        @MainThread
        NetworkBoundResource() {
            result.setValue(Resource.loading(null));
            LiveData<ResultType> 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<ResultType> dbSource) {
            LiveData<ApiResponse<RequestType>> 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<RequestType> response) {
            new AsyncTask<Void, Void, Void>() {
    
                @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<Resource<ResultType>> getAsLiveData() {
            return result;
        }
    }
    
    class UserRepository {
        Webservice webservice;
        UserDao userDao;
    
        public LiveData<Resource<User>> loadUser(final String userId) {
            return new NetworkBoundResource<User,User>() {
                @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<User> loadFromDb() {
                    return userDao.load(userId);
                }
    
                @NonNull @Override
                protected LiveData<ApiResponse<User>> createCall() {
                    return webservice.getUser(userId);
                }
            }.getAsLiveData();
        }
    }
    

    相关文章

      网友评论

          本文标题:LiveData

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