androidasynctaskloader

How to passing variables out from AsyncTaskLoader


I make a movie search application using TMDB API, but TMDB always give results in pages with 20 results each page, so I need to arrange interface for page selection.

I'm using AsyncTaskLoader to make asynchronous API request, but I don't know how to send variabels out from background process other than return value.

Here is my AsyncTaskLoader class:

public class MovieAsyncTaskLoader extends AsyncTaskLoader<ArrayList<Movie>> {

private ArrayList<Movie> movies;
private boolean hasResult = false;
private String keyword;

public MovieAsyncTaskLoader(final Context context, String keyword) {
    super(context);

    onContentChanged();
    this.keyword = keyword;
}

@Override
protected void onStartLoading() {
    if (takeContentChanged())
        forceLoad();
    else if (hasResult)
        deliverResult(movies);
}

@Override
public void deliverResult(final ArrayList<Movie> data) {
    movies = data;
    hasResult = true;
    super.deliverResult(data);
}

@Override
protected void onReset() {
    super.onReset();
    onStopLoading();
    if (hasResult) {
        onReleaseResources(movies);
        movies = null;
        hasResult = false;
    }
}

private void onReleaseResources(ArrayList<Movie> movies) {

}


private static final String API_KEY = "323ee6e4b1621c63xxxxxxxxxxxxxxxx";

@Override
public ArrayList<Movie> loadInBackground() {

    SyncHttpClient client = new SyncHttpClient();
    final ArrayList<Movie> movies = new ArrayList<>();
    String url = "https://api.themoviedb.org/3/search/movie?api_key=" +
                    API_KEY + "&language=en-US&query=" + keyword ;

    client.get( url, new AsyncHttpResponseHandler() {
        @Override
        public void onStart() {
            super.onStart();
            setUseSynchronousMode(true);
        }

        @Override
        public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
            try {
                String result = new String(responseBody);
                JSONObject responseObject = new JSONObject(result);

                // how to passing these variabels out to MainActivity ??
                Integer total_results = responseObject.getInt("total_results");
                Integer total_pages = responseObject.getInt("total_pages");
                Integer page = responseObject.getInt("page");



                JSONArray list = responseObject.getJSONArray("results");

                for (int i = 0 ; i < list.length() ; i++){
                    JSONObject movieitem = list.getJSONObject(i);
                    Movie movie = new Movie(movieitem);
                    movies.add(movie);
                }

            }catch (Exception e){
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {

        }
    });

    return movies;
  }
}

and this is my MainActivity class:

public class MainActivity extends AppCompatActivity implements 
LoaderManager.LoaderCallbacks<ArrayList<Movie>> {

MovieListAdapter adapter;
ListView movielist;
EditText etKeyword;
Button btSearch;
TextView tvFound;

public Integer total_results;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    adapter = new MovieListAdapter(this);
    adapter.notifyDataSetChanged();

    movielist = (ListView) findViewById(R.id.lvMovie);
    movielist.setAdapter( adapter );

    etKeyword = (EditText) findViewById(R.id.etKeyword);
    String keyword = etKeyword.getText().toString();

    Bundle bundle = new Bundle();
    bundle.putString("keyword", keyword);
    getLoaderManager().restartLoader(0, bundle, this);


    btSearch = (Button) findViewById( R.id.btSearch );
    btSearch.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String keyword = etKeyword.getText().toString();
            if (TextUtils.isEmpty(keyword))return;

            Bundle bundle = new Bundle();
            bundle.putString("keyword", keyword);
            getLoaderManager().restartLoader(0, bundle, MainActivity.this);
        }
    });

    tvFound = (TextView) findViewById(R.id.tvFound);

}

@Override
public Loader<ArrayList<Movie>> onCreateLoader(int i, Bundle bundle) {
    String keyword = "";
    if( bundle != null) {
        keyword = bundle.getString("keyword");
    }
    return new MovieAsyncTaskLoader(this, keyword );
}

@Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movies) {
    adapter.setDatamovie( movies );
}

@Override
public void onLoaderReset(Loader<ArrayList<Movie>> loader) {
    adapter.setDatamovie(null);
}
}

The movielist ListView works fine to show the result, but I want to put the total_results in tvFound TextView

Here is my Movie POJO class:

public class Movie implements Parcelable {

private Integer voteCount;
private Integer id;
private Boolean video;
private Double voteAverage;
private String title;
private Double popularity;
private String posterPath;
private String originalLanguage;
private String originalTitle;
private List<Integer> genreIds = null;
private String backdropPath;
private Boolean adult;
private String overview;
private String releaseDate;

public Movie(JSONObject movieobject){

    try {
        Integer votecount = movieobject.getInt("vote_count");
        Integer id = movieobject.getInt("id");
        Boolean video = movieobject.getBoolean("video");
        Double voteaverage = movieobject.getDouble("vote_average");
        String title = movieobject.getString("title");
        Double popularity = movieobject.getDouble("popularity");
        String posterpath = movieobject.getString("poster_path");
        String originallanguage = movieobject.getString("original_language");
        String originaltitle = movieobject.getString("original_title");
        List<Integer> genreid = (List<Integer>) movieobject.optJSONArray("genre_id");
        String backdroppath = movieobject.getString("backdrop_path");
        Boolean adult = movieobject.getBoolean("adult");
        String overview = movieobject.getString("overview");
        String releasedate = movieobject.getString("release_date");

        this.voteCount = votecount;
        this.id = id;
        this.video = video;
        this.voteAverage = voteaverage;
        this.title = title;
        this.popularity = popularity;
        this.posterPath = posterpath;
        this.originalLanguage = originallanguage;
        this.originalTitle = originaltitle;
        this.genreIds = genreid;
        this.backdropPath = backdroppath;
        this.adult = adult;
        this.overview = overview;
        this.releaseDate = releasedate;

    } catch (JSONException e) {
        e.printStackTrace();
    }

}

public Integer getVoteCount() {
    return voteCount;
}

public void setVoteCount(Integer voteCount) {
    this.voteCount = voteCount;
}

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public Boolean getVideo() {
    return video;
}

public void setVideo(Boolean video) {
    this.video = video;
}

public Double getVoteAverage() {
    return voteAverage;
}

public void setVoteAverage(Double voteAverage) {
    this.voteAverage = voteAverage;
}

public String getTitle() {
    return title;
}

public void setTitle(String title) {
    this.title = title;
}

public Double getPopularity() {
    return popularity;
}

public void setPopularity(Double popularity) {
    this.popularity = popularity;
}

public String getPosterPath() {
    return posterPath;
}

public void setPosterPath(String posterPath) {
    this.posterPath = posterPath;
}

public String getOriginalLanguage() {
    return originalLanguage;
}

public void setOriginalLanguage(String originalLanguage) {
    this.originalLanguage = originalLanguage;
}

public String getOriginalTitle() {
    return originalTitle;
}

public void setOriginalTitle(String originalTitle) {
    this.originalTitle = originalTitle;
}

public List<Integer> getGenreIds() {
    return genreIds;
}

public void setGenreIds(List<Integer> genreIds) {
    this.genreIds = genreIds;
}

public String getBackdropPath() {
    return backdropPath;
}

public void setBackdropPath(String backdropPath) {
    this.backdropPath = backdropPath;
}

public Boolean getAdult() {
    return adult;
}

public void setAdult(Boolean adult) {
    this.adult = adult;
}

public String getOverview() {
    return overview;
}

public void setOverview(String overview) {
    this.overview = overview;
}

public String getReleaseDate() {
    return releaseDate;
}

public void setReleaseDate(String releaseDate) {
    this.releaseDate = releaseDate;
}

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeValue(this.voteCount);
    dest.writeValue(this.id);
    dest.writeValue(this.video);
    dest.writeValue(this.voteAverage);
    dest.writeString(this.title);
    dest.writeValue(this.popularity);
    dest.writeString(this.posterPath);
    dest.writeString(this.originalLanguage);
    dest.writeString(this.originalTitle);
    dest.writeList(this.genreIds);
    dest.writeString(this.backdropPath);
    dest.writeValue(this.adult);
    dest.writeString(this.overview);
    dest.writeString(this.releaseDate);
}

protected Movie(Parcel in) {
    this.voteCount = (Integer) in.readValue(Integer.class.getClassLoader());
    this.id = (Integer) in.readValue(Integer.class.getClassLoader());
    this.video = (Boolean) in.readValue(Boolean.class.getClassLoader());
    this.voteAverage = (Double) in.readValue(Double.class.getClassLoader());
    this.title = in.readString();
    this.popularity = (Double) in.readValue(Double.class.getClassLoader());
    this.posterPath = in.readString();
    this.originalLanguage = in.readString();
    this.originalTitle = in.readString();
    this.genreIds = new ArrayList<Integer>();
    in.readList(this.genreIds, Integer.class.getClassLoader());
    this.backdropPath = in.readString();
    this.adult = (Boolean) in.readValue(Boolean.class.getClassLoader());
    this.overview = in.readString();
    this.releaseDate = in.readString();
}

public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
    @Override
    public Movie createFromParcel(Parcel source) {
        return new Movie(source);
    }

    @Override
    public Movie[] newArray(int size) {
        return new Movie[size];
    }
};
}

and this is results example from TMDB API request:

{"page":1,
 "total_results":14,
 "total_pages":1,
 "results":[
        {"vote_count":7143,
         "id":20352,
         "video":false,
         "vote_average":7.1,
         "title":"Despicable Me",
         "popularity":41.730844,
         "poster_path":"\/4zHJhBSY4kNZXfhTlmy2TzXD51M.jpg",
         "original_language":"en",
         "original_title":"Despicable Me",
         "genre_ids":[16,10751],
         "backdrop_path":"\/yo1ef57MEPkEE4BDZKTZGH9uDcX.jpg",
         "adult":false,
         "overview":"Villainous Gru lives up to ... <deleted> .",
         "release_date":"2010-07-08"},

        {"vote_count":5254,
         "id":93456,
         "video":false,
         "vote_average":7,
         "title":"Despicable Me 2",
         "popularity":56.02126,
         "poster_path":"\/kQrYyZQHkwkUg2KlUDyvymj9FAp.jpg",
         "original_language":"en",
         "original_title":"Despicable Me 2",
         "genre_ids":[16,35,10751],
         "backdrop_path":"\/rmgxcw8tGTmdhsWqdjGBS9uI1tO.jpg",
         "adult":false,
         "overview":"Gru is recruited by the Anti-Villain League to ... <deleted>.",
         "release_date":"2013-06-25"},
       ...
    ]
}

Thank You in advanced


Solution

  • You can create an interface to listen when you finish loading the movies list, and make MainActivity implement this interface.

    public class MovieAsyncTaskLoader extends AsyncTaskLoader<ArrayList<Movie>> {
    
    private ArrayList<Movie> movies;
    private boolean hasResult = false;
    private String keyword;
    
    private MoviesLoadingListener listener; //MainActivity
    
    public MovieAsyncTaskLoader(final Context context, String keyword, MoviesLoadingListener listener) {
        super(context);
    
        onContentChanged();
        this.keyword = keyword;
        this.listener = listener; //set MainActivity as the listener
    }
    
     .....
     .....
    
    @Override
    public ArrayList<Movie> loadInBackground() {
    
        SyncHttpClient client = new SyncHttpClient();
        final ArrayList<Movie> movies = new ArrayList<>();
        String url = "https://api.themoviedb.org/3/search/movie?api_key=" +
                        API_KEY + "&language=en-US&query=" + keyword ;
    
        client.get( url, new AsyncHttpResponseHandler() {
            @Override
            public void onStart() {
                super.onStart();
                setUseSynchronousMode(true);
            }
    
            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[]         responseBody) {
                try {
                    String result = new String(responseBody);
                        JSONObject responseObject = new JSONObject(result);
    
                    // how to passing these variabels out to MainActivity ??
                    Integer total_results = responseObject.getInt("total_results");
                    Integer total_pages = responseObject.getInt("total_pages");
                    Integer page = responseObject.getInt("page");
    
                    //here you pass the three integers to MainActivity
                    listener.onMoviesLoaded(total_results, total_pages, page);
    
                    JSONArray list = responseObject.getJSONArray("results");
    
                    for (int i = 0 ; i < list.length() ; i++){
                        JSONObject movieitem = list.getJSONObject(i);
                        Movie movie = new Movie(movieitem);
                        movies.add(movie);
                    }
    
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
    
            }
        });
    
    return movies;
      }
    
        //This is the interface that MainActivity will implement
        public interface MoviesLoadingListener {
            void onMoviesLoaded(int total_results, int total_pages, int page);
        }
    }
    

    MainActivity code:

     //MainActivity implements the interface we created
    public class MainActivity extends AppCompatActivity implements 
     LoaderManager.LoaderCallbacks<ArrayList<Movie>>,
     MovieAsyncTaskLoader.MoviesLoadingListener {
    
        ......
        ......
    
        @Override
        public Loader<ArrayList<Movie>> onCreateLoader(int i, Bundle bundle) {
            String keyword = "";
            if( bundle != null) {
                keyword = bundle.getString("keyword");
            }
            //here you pass the activity as the listener in the third parameter
            return new MovieAsyncTaskLoader(this, keyword, this );
        }
    
        ......
        ......
    
        @Override
        public void onMoviesLoaded(int total_results, int total_pages, int page) {
            //do whatever you need with the three integers here
        }    
    
        ......
        ......
    }