如何从 AsyncTaskLoader 传递变量

How to passing variables out from AsyncTaskLoader

我用TMDB做了一个电影搜索应用程序API,但是TMDB总是分页给出结果,每页有20个结果,所以我需要安排页面选择的界面。

我正在使用 AsyncTaskLoader 发出异步 API 请求,但我不知道如何从 return 值以外的后台进程发送变量。

这是我的 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;
  }
}

这是我的 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);
}
}

movielist ListView 可以很好地显示结果,但我想将 total_results 放在 [=16= 中]文本视图

这是我的 电影 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];
    }
};
}

这是来自 TMDB API 请求的结果示例:

{"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"},
       ...
    ]
}

提前致谢

您可以创建一个接口,在加载完电影列表后监听,并让MainActivity实现这个接口。

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代码:

 //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
    }    

    ......
    ......
}

好吧,我知道这不是你想听到的,但我不会使用 AsyncTaskLoader 因为它不是 HttpRequests 的理想选择,因为你会迷失在代码中。你能做的最好的事情就是花点时间看看这个由 Square http://square.github.io/retrofit/ 编写的库。

这也避免了让您发疯的 JSON 转换。你可以使用 GSON,这是一个很棒的工具,它也被 Retrofit 支持。

最后我注意到您正在使用 Parcel 序列化和反序列化您的对象。我建议您使用自动为您生成读取和写入的 Parceler。

这些东西将帮助您的代码保持可读性和简洁性。希望我有所帮助:)