Я создавал пример для понимания чистой архитектуры Android с использованием MVVM.У меня есть Interactor / use case, который извлекает список предлагаемых элементов из локальной базы данных, и я устанавливаю его результат в оперативные данные, которые наблюдаются фрагментом.Ниже приведены мои коды.Проблема в том, что я запрашиваю локальную базу данных при каждом нажатии клавиши.Я хочу оптимизировать это поведение в случае, если мне нужно было получить результат с какого-либо сервера.
GetCitiesByNameInteractor
public class GetCitiesByNameInteractor extends BaseUseCase<Result<List<City>>, String> {
private final CityRepository<Boolean, City> cityRepository;
@Inject
public GetCitiesByNameInteractor(final Logger logger, final CityRepository<Boolean, City> cityRepository, final ThreadScheduler threadScheduler) {
super(logger, threadScheduler.computation(), threadScheduler.computation());
this.cityRepository = cityRepository;
}
@Override
protected Observable<Result<List<City>>> getUseCase(final String cityName) {
return cityRepository.getData(cityName);
}
}
BaseUseCase
public abstract class BaseUseCase<RESULT, PARAM> {
private final String operationName;
private final Scheduler observableScheduler;
private final Scheduler observerScheduler;
private static CompositeDisposable compositeDisposable = getCompositeDisposable();
private static Map<String, DisposableObserver> disposableObserverMap = new HashMap<>();
private Logger logger;
protected BaseUseCase(Logger logger, Scheduler observableScheduler, Scheduler observerScheduler){
this.operationName = this.getClass().getSimpleName();
this.observableScheduler = observableScheduler;
this.observerScheduler = observerScheduler;
this.logger = logger;
}
public void execute(DisposableObserver<RESULT> disposableObserver, PARAM param){
compositeDisposable = getCompositeDisposable();
DisposableObserver savedDisposable = disposableObserverMap.get(operationName);
if(savedDisposable != null){
compositeDisposable.remove(savedDisposable);
}
compositeDisposable.add(disposableObserver);
disposableObserverMap.put(operationName, disposableObserver);
getUseCase(param)
.subscribeOn(observerScheduler)
.observeOn(observableScheduler)
.subscribeWith(disposableObserver);
}
private static CompositeDisposable getCompositeDisposable(){
if(compositeDisposable == null || compositeDisposable.isDisposed()){
compositeDisposable = new CompositeDisposable();
}
return compositeDisposable;
}
protected abstract Observable<RESULT> getUseCase(PARAM param);
public static void stopAll(){
if(compositeDisposable != null && !compositeDisposable.isDisposed()){
disposableObserverMap.clear();
compositeDisposable.dispose();
}
}
public String getOperationName() {
return operationName;
}
}
CityViewModel
public class CityViewModel extends ViewModel implements CityView<List<City>> {
private final GetCitiesByNameInteractor citiesByNameInteractor;
private final Logger logger;
private MutableLiveData<List<City>> cityListData = new MutableLiveData<>();
private MutableLiveData<String> emptyData = new MutableLiveData<>();
private MutableLiveData<Boolean> progressData = new MutableLiveData<>();
private MutableLiveData<String> noInternetErrorData = new MutableLiveData<>();
private MutableLiveData<String> genericErrorData = new MutableLiveData<>();
@Inject
public CityViewModel(GetCitiesByNameInteractor citiesByNameInteractor, Logger logger) {
this.citiesByNameInteractor = citiesByNameInteractor;
this.logger = logger;
}
@Override
public void search(String city) {
citiesByNameInteractor.execute(new BaseDisposableSubscriber<List<City>>(citiesByNameInteractor.getOperationName(), logger) {
@Override
public void onNext(Result<List<City>> listResult) {
super.onNext(listResult);
switch (listResult.getResultType()){
case SUCCESS:
handleSuccess(listResult.getData());
break;
case ERROR:
handleError(listResult.getError());
break;
case PROGRESS:
handleProgress(listResult.isLoading());
}
}
@Override
public void onError(Throwable e) {
super.onError(e);
handleError(e);
}
}, city);
}
private void handleProgress(boolean loading) {
progressData.postValue(loading);
}
private void handleSuccess(List<City> data) {
if(data == null){
return;
}
if(data.isEmpty()){
emptyData.postValue("Empty data");
} else {
Collections.sort(data, cityComparator);
cityListData.postValue(data);
}
}
private Comparator<City> cityComparator = new Comparator<City>() {
@Override
public int compare(City city1, City city2) {
return city1.getName().compareTo(city2.getName());
}
};
private void handleError(Throwable throwable){
if(throwable == null){
return;
}
if(throwable instanceof NoInternetError){
noInternetErrorData.postValue(throwable.getMessage());
} else {
genericErrorData.postValue(throwable.getMessage());
}
}
@Override
public LiveData<List<City>> getCityLiveData() {
return cityListData;
}
@Override
public LiveData<String> getEmptyLiveData() {
return emptyData;
}
@Override
public LiveData<Boolean> getProgressLiveData() {
return progressData;
}
@Override
public LiveData<String> getNoInternetLiveData() {
return noInternetErrorData;
}
@Override
public LiveData<String> getGenericErrorLiveData() {
return genericErrorData;
}
}
MainFragment
public class MainFragment extends Fragment implements Injectable, CityListener {
private static final String TAG = "MainFragment";
@Inject
ViewModelProvider.Factory viewModelFactory;
@Inject
Logger logger;
@Inject
Stringify stringify;
private CityViewModel cityViewModel;
private CitySuggestionAdapter citySuggestAdapter;
public static Fragment getInstance(){
MainFragment fragment = new MainFragment();
return fragment;
}
@BindView(R.id.et_search)
EditText searchEt;
@BindView(R.id.progress_search)
ProgressBar searchProgress;
@BindView(R.id.rv_search)
RecyclerView searchRv;
@BindView(R.id.tv_message)
TextView tvMessage;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
cityViewModel = ViewModelProviders.of(this, viewModelFactory).get(CityViewModel.class);
cityViewModel.getCityLiveData().observe(this, cityListObserver);
cityViewModel.getEmptyLiveData().observe(this, emptyDataObserver);
cityViewModel.getProgressLiveData().observe(this, progressObserver);
cityViewModel.getNoInternetLiveData().observe(this, noInternetObserver);
cityViewModel.getGenericErrorLiveData().observe(this, genericErrorObserver);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tvMessage.setVisibility(View.GONE);
citySuggestAdapter = new CitySuggestionAdapter(this, logger);
searchRv.setLayoutManager(new LinearLayoutManager(this.getContext(), LinearLayoutManager.VERTICAL, false));
searchRv.setAdapter(citySuggestAdapter);
searchRv.setHasFixedSize(true);
searchEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if(editable != null && editable.length() > 0){
cityViewModel.search(editable.toString());
}
}
});
}
private Observer<List<City>> cityListObserver = new Observer<List<City>>() {
@Override
public void onChanged(@Nullable List<City> cities) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "Cities: %s", cities);
setMessageVisibility(false);
citySuggestAdapter.submitList(cities);
}
};
private void setMessageVisibility(boolean isError){
tvMessage.setVisibility(isError ? View.VISIBLE: View.GONE);
searchRv.setVisibility(isError ? View.GONE : View.VISIBLE);
}
private Observer<Boolean> progressObserver = new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean isInProgress) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "Progress: %s", isInProgress);
searchProgress.setVisibility(isInProgress ? View.VISIBLE: View.GONE);
}
};
private Observer<String> emptyDataObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable String message) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "Empty Message: %s", message);
setMessageVisibility(true);
tvMessage.setText(message);
}
};
private Observer<String> noInternetObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable String message) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "No Internet Message: %s", message);
setMessageVisibility(true);
tvMessage.setText(message);
}
};
private Observer<String> genericErrorObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable String message) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "Generic Error Message: %s", message);
setMessageVisibility(true);
tvMessage.setText(message);
}
};
@Override
public void onCitySelected(City city) {
logger.printObject(TAG, Logger.LogLevel.DEBUG, "Selected City: %s", city);
setMessageVisibility(true);
try {
tvMessage.setText(stringify.getString("%s", city));
} catch (FormatException e) {
e.printStackTrace();
}
}
@Override
public String getSearchedText() {
return searchEt != null ? searchEt.getText().toString() : "";
}
}