Как использовать предложения поиска в Mvvm с Rxjava - PullRequest
0 голосов
/ 02 октября 2018

Я создавал пример для понимания чистой архитектуры 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() : "";
    }
}
...