我浏览了许多可用于新架构组件的示例代码,但我在设置项目时仍然面临一些问题。
我需要从我的远程服务中获取数据并将其保存到房间数据库中。我希望我的 View 只观察一个实时数据列表。我的 AppRepository 处理 RemoteRepository 和 LocalRepository。远程存储库有一个 fetchMovies() 方法,它从网络服务接收电影列表。我想将此列表保存在房间数据库中,目前我的 RemoteRepository 类是这样做的。
public void fetchMovieFromRemote(int page){
movieService.fetchPopularMovies(ApiConstants.BASE_URL, page).enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
int statusCode = response.code();
if (statusCode == 200){
mLocalRepository.insert(response.body());
}
}
@Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
}
});
}
根据我的理解,理想情况下,远程和本地存储库应该是独立的,这项工作应该由 AppRepository 类完成。
一种方法是使用回调,但我想使用实时数据来做到这一点。 fetchMovieFromRemote(int page) 方法是否应该为此返回实时数据,但在这种情况下如何在我的 View 模型中处理它,该 View 模型目前具有由房间返回的电影列表的实时数据。
@Query("SELECT * from movie ORDER BY id ASC")
LiveData<List<Movie>> getAllWords();
我是 MVVM 的新手,请指导我了解这种架构的理想方法是什么。
最佳答案
我采用了 Google 在他们的示例中用于存储库的模式,该模式为您提供单一的真实来源(您的 Room 数据库)。
在这里讨论:https://developer.android.com/jetpack/docs/guide
要注意的关键部分是 NetworkBoundResource 类(Google 示例:https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/repository/NetworkBoundResource.kt)。这个来自 Google 的例子在 Kotlin 中,我确实找到了一个 Java 例子。
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package iammert.com.androidarchitecture.data;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
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) {
result.addSource(dbSource, newData -> result.setValue(Resource.loading(newData)));
createCall().enqueue(new Callback<RequestType>() {
@Override
public void onResponse(Call<RequestType> call, Response<RequestType> response) {
result.removeSource(dbSource);
saveResultAndReInit(response.body());
}
@Override
public void onFailure(Call<RequestType> call, Throwable t) {
onFetchFailed();
result.removeSource(dbSource);
result.addSource(dbSource, newData -> result.setValue(Resource.error(t.getMessage(), newData)));
}
});
}
@MainThread
private void saveResultAndReInit(RequestType response) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
saveCallResult(response);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
result.addSource(loadFromDb(), newData -> result.setValue(Resource.success(newData)));
}
}.execute();
}
@WorkerThread
protected abstract void saveCallResult(@NonNull RequestType item);
@MainThread
protected boolean shouldFetch(@Nullable ResultType data) {
return true;
}
@NonNull
@MainThread
protected abstract LiveData<ResultType> loadFromDb();
@NonNull
@MainThread
protected abstract Call<RequestType> createCall();
@MainThread
protected void onFetchFailed() {
}
public final LiveData<Resource<ResultType>> getAsLiveData() {
return result;
}
}
这是使用该类的 repo :
package iammert.com.androidarchitecture.data;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;
import java.util.List;
import javax.inject.Inject;
import iammert.com.androidarchitecture.data.local.dao.MovieDao;
import iammert.com.androidarchitecture.data.local.entity.MovieEntity;
import iammert.com.androidarchitecture.data.remote.MovieDBService;
import iammert.com.androidarchitecture.data.remote.model.MoviesResponse;
import retrofit2.Call;
/**
* Created by mertsimsek on 19/05/2017.
*/
public class MovieRepository {
private final MovieDao movieDao;
private final MovieDBService movieDBService;
@Inject
public MovieRepository(MovieDao movieDao, MovieDBService movieDBService) {
this.movieDao = movieDao;
this.movieDBService = movieDBService;
}
public LiveData<Resource<List<MovieEntity>>> loadPopularMovies() {
return new NetworkBoundResource<List<MovieEntity>, MoviesResponse>() {
@Override
protected void saveCallResult(@NonNull MoviesResponse item) {
movieDao.saveMovies(item.getResults());
}
@NonNull
@Override
protected LiveData<List<MovieEntity>> loadFromDb() {
return movieDao.loadMovies();
}
@NonNull
@Override
protected Call<MoviesResponse> createCall() {
return movieDBService.loadMovies();
}
}.getAsLiveData();
}
public LiveData<MovieEntity> getMovie(int id){
return movieDao.getMovie(id);
}
}
我将尝试简要解释一下,您的 Repo 中有一个方法,例如 loadMovies(),它从您的存储库中返回电影的 LiveData 列表。使用 NetworkBoundResource,首先检查 Room 数据库,然后查询 API,然后将结果加载到数据库中。数据库更新后,您正在观察的 LiveData 将使用新结果进行更新。
此图显示了这背后的逻辑流程:
正如您在上面看到的,您正在观察磁盘的变化,并在它发生变化时收到更新。
我建议通读我之前链接的那个 Jetpack 指南,因为他们会更详细地解释它。我相信这就是你要找的。
关于android-room - Android 架构组件(MVVM) - 使用存储库模式处理远程和本地数据的理想方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51865202/