Search code examples
androidrx-android

How Do I Use Variable Outside Subscribe


I tried to loop an Api call based on an Array size, and put the result into an adapter. But I get null when I tried to use the results of the api call because it is outside a subscribe. how do I use the variable outside the subscribe?

Here's my Code

    private void getMoviesData(final String lang) {
    favouriteMovies = databaseHelper.getAllFavouriteMovies();
    for (int i = 0; i < favouriteMovies.size(); i++) {
        api.getSingleMovie(favouriteMovies.get(i).getMovieId(),apiKey, lang)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Observer<SingleMovieResponse>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        progressBar.setVisibility(View.GONE);
                        Log.d(TAG, "Show Data Error!: " + e);
                    }

                    @Override
                    public void onNext(SingleMovieResponse singleMovieResponse) {

                        for (int i = 0; i < singleMovieResponse.getSpoken_languages().size(); i++ ) {
                            MoviesObject moviesObject = new MoviesObject();
                            moviesObject.setMovieId(singleMovieResponse.getId());
                            moviesObject.setTitle(singleMovieResponse.getTitle());
                            moviesObject.setPosterPath(singleMovieResponse.getPoster_path());
                            moviesObject.setOverview(singleMovieResponse.getOverview());
                            moviesObject.setReleaseDate(singleMovieResponse.getRelease_date());
                            moviesObject.setVoteAverage(singleMovieResponse.getVote_average());
                            if (singleMovieResponse.getOverview().equals("")) {
                                moviesObject.setOverview(Objects.requireNonNull(getActivity()).getResources().getString(R.string.no_overview_movie));
                            }
                            movies.add(moviesObject);
                        }

                    }
                });
    }
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    MovieAdapter movieAdapter = new MovieAdapter(movies,getActivity());
    recyclerView.setAdapter(movieAdapter);
    recyclerView.setHasFixedSize(true);
    progressBar.setVisibility(View.GONE);
    recyclerView.setVisibility(View.VISIBLE);
}

how to get movies values outside the subscribe?


Solution

  • You don't. The result from the stream is asynchronous, which means that it will be called some time later, while you setting the adapter with data is synchronous. Also, don't use a loop when handling streams, use the Reactive operators as shown below. I can't compile to test whether it works, but below is roughly the correct answer to your question.

    private void getMoviesData(final String lang) {
            favouriteMovies = databaseHelper.getAllFavouriteMovies();
            Observable.fromIterable(favouriteMovies) // Creates a stream of Favourite Movies
                    .flatMap(new Function<FavouriteMovie, ObservableSource<?>>() { // Makes a request for each FavouriteMovie 
                        @Override
                        public ObservableSource<?> apply(FavouriteMovie favouriteMovie) throws Exception {
                            return api.getSingleMovie(favouriteMovie.getMovieId(), apiKey, lang);
                        }
                    })
                    .map(new Function<SingleMovieResponse, MoviesObject>() { // Maps the response to the wanted object
                        @Override
                        public MoviesObject apply(SingleMovieResponse singleMovieResponse) throws Exception {
                            return map(singleMovieResponse);
                        }
                    })
                    .toList() // Merges each item of the Observable stream into a Single stream as List<Object>
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeOn(Schedulers.io())
                    .subscribe(new SingleObserver<List<MoviesObject>>() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onSuccess(List<MoviesObject> moviesObjects) {
                            progressBar.setVisibility(View.GONE);
                            recyclerView.setVisibility(View.VISIBLE);
                            movieAdapter.setList(moviesObjects); // Create this method in the adapter to update list
                            movieAdapter.notifyDataSetChanged();
                        }
    
                        @Override
                        public void onError(Throwable e) {
                            progressBar.setVisibility(View.GONE);
                            Log.d(TAG, "Show Data Error!: " + e);
                        }
                    });
    
            movieAdapter = new MovieAdapter(movies,getActivity()); // MovieAdapter should be a class field
            recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
            recyclerView.setHasFixedSize(true);
            recyclerView.setAdapter(movieAdapter);
        }
    
        private MoviesObject map(SingleMovieResponse singleMovieResponse) {
            MoviesObject moviesObject = new MoviesObject();
            moviesObject.setMovieId(singleMovieResponse.getId());
            moviesObject.setTitle(singleMovieResponse.getTitle());
            moviesObject.setPosterPath(singleMovieResponse.getPoster_path());
            moviesObject.setOverview(singleMovieResponse.getOverview());
            moviesObject.setReleaseDate(singleMovieResponse.getRelease_date());
            moviesObject.setVoteAverage(singleMovieResponse.getVote_average());
            if (singleMovieResponse.getOverview().equals("")) {
                moviesObject.setOverview(Objects.requireNonNull(getActivity()).getResources().getString(R.string.no_overview_movie));
            }
            return moviesObject;
        }
    

    P.S. Try introducing lambda expressions to make your life easier, or even better Kotlin!!!