publicclassUserRepository{ private Webservice webservice; // ... public LiveData<User> getUser(int userId){ // This is not an optimal implementation, we'll fix it below final MutableLiveData<User> data = new MutableLiveData<>(); webservice.getUser(userId).enqueue(new Callback<User>() { @Override publicvoidonResponse(Call<User> call, Response<User> response){ // error case is left out for brevity data.setValue(response.body()); } }); return data; } }
@Inject// UserRepository parameter is provided by Dagger 2 publicUserProfileViewModel(UserRepository userRepo){ this.userRepo = userRepo; }
publicvoidinit(String userId){ if (this.user != null) { // ViewModel is created per Fragment so // we know the userId won't change return; } user = userRepo.getUser(userId); }
public LiveData<User> getUser(){ returnthis.user; } }
4. 数据缓存
为UserRepository 添加一个缓存,避免重复的网络请求。
@Singleton// informs Dagger that this class should be constructed once publicclassUserRepository{ private Webservice webservice; // simple in memory cache, details omitted for brevity private UserCache userCache; public LiveData<User> getUser(String userId){ LiveData<User> cached = userCache.get(userId); if (cached != null) { return cached; }
final MutableLiveData<User> data = new MutableLiveData<>(); userCache.put(userId, data); // this is still suboptimal but better than before. // a complete implementation must also handle the error cases. webservice.getUser(userId).enqueue(new Callback<User>() { @Override publicvoidonResponse(Call<User> call, Response<User> response){ data.setValue(response.body()); } }); return data; } }
@Database(entities = {User.class}, version = 1) publicabstractclassMyDatabaseextendsRoomDatabase{ }
MyDatabase 这里被声明为 abstract. Room 会自动的提供各种实现. See Room
为了插入UserData,还得创建一个DAO(data access object)
@Dao publicinterfaceUserDao{ @Insert(onConflict = REPLACE) voidsave(User user); @Query("SELECT * FROM user WHERE id = :userId") LiveData<User> load(String userId); }
public LiveData<User> getUser(String userId){ refreshUser(userId); // return a LiveData directly from the database. return userDao.load(userId); }
privatevoidrefreshUser(final String userId){ executor.execute(() -> { // running in a background thread // check if user was fetched recently boolean userExists = userDao.hasUser(FRESH_TIMEOUT); if (!userExists) { // refresh the data Response response = webservice.getUser(userId).execute(); // TODO check for error etc. // Update the database.The LiveData will automatically refresh so // we don't need to do anything else here besides updating the database userDao.save(response.body()); } }); } }
//a generic class that describes a data with a status publicclassResource<T> { @NonNullpublicfinal Status status; @Nullablepublicfinal T data; @Nullablepublicfinal String message; privateResource(@NonNull Status status, @Nullable T data, @Nullable String message){ this.status = status; this.data = data; this.message = message; }
// ResultType: Type for the Resource data // RequestType: Type for the API response publicabstractclassNetworkBoundResource<ResultType, RequestType> { // Called to save the result of the API response into the database @WorkerThread protectedabstractvoidsaveCallResult(@NonNull RequestType item);
// Called with the data in the database to decide whether it should be // fetched from the network. @MainThread protectedabstractbooleanshouldFetch(@Nullable ResultType data);
// Called to get the cached data from the database @NonNull@MainThread protectedabstract LiveData<ResultType> loadFromDb();
// Called to create the API call. @NonNull@MainThread protectedabstract LiveData<ApiResponse<RequestType>> createCall();
// Called when the fetch fails. The child class may want to reset components // like rate limiter. @MainThread protectedvoidonFetchFailed(){ }
// returns a LiveData that represents the resource publicfinal LiveData<Resource<ResultType>> getAsLiveData() { return result; } }
publicabstractclassNetworkBoundResource<ResultType, RequestType> { privatefinal MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
@Override protectedvoidonPostExecute(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(); } }
最后,在UserRepository中使用NetworkBoundResource
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); }