1

I would like to implement ProgressBar for my server delay.

I am based on :

Consuming REST API using Retrofit Library with the help of MVVM, Dagger 2, LiveData and RxJava 2 in Android

https://medium.com/@saquib3705/consuming-rest-api-using-retrofit-library-with-the-help-of-mvvm-dagger-livedata-and-rxjava2-in-67aebefe031d

I do some work but I have several issue (LogCat at bottom)

my code (edited - full view in my ViewModel Class)

public class ViewModelApp extends ViewModel {

    //services
    private ServiceItune serviceItune;
    private ServiceLastFm serviceLastFm;

    @Inject
    @Named("apiLastFm")
    Retrofit apiLastFm;

    @Inject
    @Named("apiItune")
    Retrofit apiItune;


    // Rx
    private final CompositeDisposable disposables = new CompositeDisposable();
    Disposable disposable;

    // LifeData
    private MutableLiveData<ApiResponse> responseLiveDataCombinedResult;

    public MutableLiveData<ApiResponse> getResponseLiveDataCombinedResult () {

        if (responseLiveDataCombinedResult == null){
            responseLiveDataCombinedResult = new MutableLiveData<ApiResponse>();
        }
        return responseLiveDataCombinedResult;
    }

    // Constructor
    public ViewModelApp() {

        MainApplication.component.inject(this);
        serviceItune = apiItune.create(ServiceItune.class);
        serviceLastFm = apiLastFm.create(ServiceLastFm.class);
        getAll("");

    }

    public Observable getMergedObservable (String query) {

        Observable<RootiTune> iTuneObservable =serviceItune.getItuneArtistNameRx2NoList(ServiceItune.API_ITUNE_BASE_FULL_URL,query);
        Observable <RootLastFm>  lastFmObservable = serviceLastFm.searchArtistRx(ServiceLastFm.API_LAST_FM_FULL_URL, query, ServiceLastFm.KEY_LAST_FM,"json");

        return iTuneObservable.flatMap((Function<RootiTune, ObservableSource<CombinedResult>>)
                rootiTune -> {
                    return Observable.just(rootiTune).zipWith(lastFmObservable,
                            (rootiTune1, rootLastFm) -> new CombinedResult(rootiTune1, rootLastFm));
                });
    }

    public void getAll (String query){

        disposables.add(getMergedObservable(query)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                //  do something (update the UI) before the task started
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) throws Exception {
                        responseLiveDataCombinedResult.setValue(ApiResponse.loading());
                        Log.v("Disposablevariable ",disposable.toString());
                    }
                })
                .subscribe((Consumer<CombinedResult>) combinedResult -> responseLiveDataCombinedResult.setValue(ApiResponse.success(combinedResult))
                        ,(Consumer<Throwable>)throwable -> responseLiveDataCombinedResult.setValue(ApiResponse.error(throwable))
                )
        );
    }

    @Override
    protected void onCleared() {
        disposables.clear();
    }
}

+

    public class ApiResponse {

        public final Status status;

        @Nullable
        public final CombinedResult combinedResult;

        @Nullable
        public final Throwable error;

        private ApiResponse(Status status, @Nullable CombinedResult combinedResult, @Nullable Throwable error) {
            this.status = status;
            this.combinedResult = combinedResult;
            this.error = error;
        }

        public static ApiResponse loading() {

            return new ApiResponse(LOADING, null, null);
        }

        public static ApiResponse success(@NonNull CombinedResult combinedResult) {
            return new ApiResponse(SUCCESS, combinedResult, null);
        }

        public static ApiResponse error(@NonNull Throwable error) {
            return new ApiResponse(ERROR, null, error);
        }

    }

my http client:

@Provides
@Singleton
public OkHttpClient okHttpClient(HttpLoggingInterceptor loggingInterceptor) {


    return new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(chain -> {
                Request original = chain.request();
                Request request = original.newBuilder()
                        .build();
                return chain.proceed(request);
            })
            .connectTimeout(100, TimeUnit.SECONDS)
            .readTimeout(300, TimeUnit.SECONDS)
            .build();
}

and finally:

class MainListFragment

   public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        viewModel =
                ViewModelProviders
                .of(getActivity())
                .get(ViewModelApp.class);

        observeViewModel(viewModel);
    }

    private void observeViewModel(ViewModelApp viewModelAppe) {

        viewModelAppe.getResponseLiveDataCombinedResult().observe(getActivity(), this::consumeResponse);

    }

    private void consumeResponse(ApiResponse apiResponse) {

        switch (apiResponse.status){

            case LOADING:

                progressDialog.show();
                break;

            case SUCCESS:
                progressDialog.dismiss();
                if (apiResponse.combinedResult.getArtistsWithPhoto() != null){
                    mainAdapter.setProjectList(apiResponse.combinedResult.getArtistsWithPhoto());
                    combinedLocalResult = apiResponse.combinedResult;
                }


            case ERROR:
                if (!Constant.checkInternetConnection(getActivity())) {
                    progressDialog.dismiss();
                    Toast.makeText(getActivity(), getResources().getString(R.string.errorString), Toast.LENGTH_SHORT).show();
                }
            default:

                break;
        }

    }

my LogCat:

 Attempt to invoke virtual method 'void android.arch.lifecycle.MutableLiveData.setValue(java.lang.Object)' on a null object reference
htw
  • 1,065
  • 7
  • 11
  • you need to initialize `responseLiveDataCombinedResult` variable in `ViewModelApp` before using it – ConstOrVar Nov 12 '18 at 08:26
  • in my Constructor ViewModelApp() I add responseLiveDataCombinedResult = Collections.emptyList(); but without success. How to initialize it default ? I also check my disposable variable in Log and it said: Disposablevariable: 0 – htw Nov 12 '18 at 10:07
  • Did you set `private MutableLiveData responseLiveDataCombinedResult = new MutiableLiveData<>();` in `ViewModelApp`? – ConstOrVar Nov 12 '18 at 11:34
  • yes, I will attach (edit) to pass full code of ViewModelApp, for better understanding – htw Nov 12 '18 at 13:18
  • 1
    you create `livedata` variable in `getResponseLiveDataCombinedResult()`, but try access it directly (not using that method) in rx chain - that's why you receive NPE. – ConstOrVar Nov 12 '18 at 14:20
  • perfect ! it,s working, not like I assume, because loader is active when app has first launch but its working ... you are genius ! – htw Nov 12 '18 at 14:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183502/discussion-between-htw-and-constorvar). – htw Nov 12 '18 at 15:00

1 Answers1

1

To resolve that problem you need to initialize responseLiveDataCombinedResult variable inside ViewModelApp. You can do it like that

private MutableLiveData<ApiResponse> responseLiveDataCombinedResult = new MutableLiveData<ApiResponse>();

or if you don't want do it for some reason, you need access that live data variable through call of getResponseLiveDataCombinedResult().

public void getAll (String query){

    disposables.add(getMergedObservable(query)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            //  do something (update the UI) before the task started
            .doOnSubscribe(new Consumer<Disposable>() {
                @Override
                public void accept(Disposable disposable) throws Exception {
                    getResponseLiveDataCombinedResult().setValue(ApiResponse.loading());
                    Log.v("Disposablevariable ",disposable.toString());
                }
            })
            .subscribe((Consumer<CombinedResult>) combinedResult -> getResponseLiveDataCombinedResult().setValue(ApiResponse.success(combinedResult))
                    ,(Consumer<Throwable>)throwable -> getResponseLiveDataCombinedResult().setValue(ApiResponse.error(throwable))
            )
    );
}

Both ways are acceptable. Also it would be more clear if your rewrite your Rx chain in that way

getMergedObservable(query)
    .map(result -> ApiResponse.success(result))
    .onErrorReturn(error -> ApiResponse.error(error))
    .startWith(ApiResponse.loading())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(response -> getResponseLiveDataCombinedResult().setValue(response), error -> {/* should not happen*/})
ConstOrVar
  • 2,025
  • 1
  • 11
  • 14