我浏览了许多可用于新体系结构组件的示例代码,但是在设置项目时仍然遇到一些问题。
我需要从远程服务中获取数据并将其保存到会议室数据库中。我希望我的视图仅观察一个实时数据列表。我的AppRepository处理RemoteRepository和LocalRepository。远程存储库具有fetchMovies()方法,该方法从Web服务接收电影列表。我想将此列表保存在会议室数据库中,目前我的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)方法是否应该为此返回一个实时数据,但是在那种情况下,如何在我的viewmodel中处理它,该模型目前具有房间返回的电影列表的实时数据。
@Query("SELECT * from movie ORDER BY id ASC")
LiveData<List<Movie>> getAllWords();
我是MVVM的新手,请引导我了解该架构的理想方法。
答案 0 :(得分:1)
让存储库完成获取数据的任务。最好让它们独立。因此,让调用活动获取数据(使用远程存储库获取电影)。然后从“活动”中使用LocalRepository更新数据库。我已经使用RxJava
进行了Api呼叫。
public interface onSuccessListener {
void onSuccess(String response);
}
在gradle中为RxJava
和RxAndroid
添加以下依赖项的最新版本。 room-rxjava可能会有帮助
// RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.1.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'android.arch.persistence.room:rxjava2:1.0.0'
您的活动
class YourActivity {
int page;
RemoteRepository remoteRepo;
LocalViewModel localViewModel;
// Initialise the above variables in the onCreate Method
private void onCreate() {
createObserver();
loadMovies();
}
.
.
.
void loadMovies() {
remoteRepo = new RemoteRepository();
Observable<String> stringObservable = Observable.defer(new Callable<ObservableSource<? extends String>>() {
@Override
public ObservableSource<? extends String> call() throws Exception {
return remoteRepo.fetchMovies();
}
});
io.reactivex.Observer<String> stringObserver = new io.reactivex.Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
// show loading dialog
}
@Override
public void onNext(String response) {
// Local repository update can be done here
localRepo.insert(response);
}
@Override
public void onError(Throwable e) {
// Handle the Error(Exception thrown)
}
@Override
public void onComplete() {
}
};
stringObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(stringObserver);
}
void createObserver() {
localViewModel.getLiveDataVariable().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String response) {
// Now this response contains the updated movies.
}
})
}
}
LocalViewModel.class
class LocalViewModel {
LocalRepository localRepo;
LiveData<String> liveDataVariable;
//constructor
LocalViewMoel(Application application) {
localRepo = new LocalRepository(application);
}
public LiveData<String> getLiveDataVariable() {
if(liveDataVariable == null) {
liveDataVariable = localRepo.queryToGetTheLiveDataVariable();
}
return liveDataVariable;
}
}
RemoteRepository现在正在进行同步调用。如果响应成功,则返回字符串,否则抛出Error(Exception)
RemoteRepository.java
class RemoteRepository {
public String fetchMovieFromRemote(int page, onSuccessListener successListener) throws Exception{
okhttp3.Response response movieService.fetchPopularMovies(ApiConstants.BASE_URL, page).execute();
ResponseBody body = response.body();
if (!response.isSuccessful()) {
throw new Exception("An error occured");
}
String result = body.string();
return result;
}
答案 1 :(得分:0)
我采用了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;
}
}
这是使用该类的存储库:
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);
}
}
我将尝试简要地解释一下,您的存储库中有一个方法,例如loadMovies(),它从存储库中返回电影的LiveData列表。使用NetworkBoundResource,首先检查Room数据库,然后查询API,然后将结果加载到数据库中。更新数据库后,将使用新结果更新您正在观察的LiveData。
此图显示了其背后的逻辑流程:
如上面所见,您正在观察磁盘上的更改,并在更改时收到更新。
我建议您通读我之前链接的Jetpack指南,因为他们会更详细地解释它。我相信这就是您想要的。
答案 2 :(得分:0)
视图模型和存储库之间的两种通信方式是
Transformations具有类似于RxJava的map和switchMap运算符 可以将实时数据转换为其他数据
您还可以使用Mediator livedata创建自己的自定义 操作员。 MediatorLiveData用于观察多个实时数据 源并在那里进行更改。
有关MVVVM和实时数据的更多信息