如何从AsyncTaskLoader传递额外变量?TMDB电影搜索应用问题
解决AsyncTaskLoader传递分页参数到MainActivity的问题
没问题,这个场景很常见,核心思路就是把电影列表和分页相关参数封装到一个自定义类中,替代原来只返回ArrayList<Movie>的方式,这样就能一次性传递所有需要的数据了。下面是具体的实现步骤:
步骤1:创建封装类MovieSearchResult
这个类用来统一存放API返回的所有关键信息,包括电影列表、总结果数、总页数、当前页码:
public class MovieSearchResult { private ArrayList<Movie> movies; private int totalResults; private int totalPages; private int currentPage; // 构造方法 public MovieSearchResult(ArrayList<Movie> movies, int totalResults, int totalPages, int currentPage) { this.movies = movies; this.totalResults = totalResults; this.totalPages = totalPages; this.currentPage = currentPage; } // Getter方法,按需获取数据 public ArrayList<Movie> getMovies() { return movies; } public int getTotalResults() { return totalResults; } public int getTotalPages() { return totalPages; } public int getCurrentPage() { return currentPage; } }
步骤2:修改MovieAsyncTaskLoader的泛型和返回逻辑
把Loader的泛型从ArrayList<Movie>改成MovieSearchResult,并在loadInBackground中解析所有分页参数,封装后返回:
// 修改泛型为MovieSearchResult public class MovieAsyncTaskLoader extends AsyncTaskLoader<MovieSearchResult> { private MovieSearchResult searchResult; 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(searchResult); } @Override public void deliverResult(final MovieSearchResult data) { searchResult = data; hasResult = true; super.deliverResult(data); } @Override protected void onReset() { super.onReset(); onStopLoading(); if (hasResult) { onReleaseResources(searchResult); searchResult = null; hasResult = false; } } private void onReleaseResources(MovieSearchResult searchResult) { } private static final String API_KEY = "323ee6e4b1621c63xxxxxxxxxxxxxxxx"; @Override public MovieSearchResult loadInBackground() { SyncHttpClient client = new SyncHttpClient(); final ArrayList<Movie> movies = new ArrayList<>(); // 初始化分页参数,避免空指针 int totalResults = 0; int totalPages = 0; int currentPage = 0; 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); // 解析分页参数 totalResults = responseObject.getInt("total_results"); totalPages = responseObject.getInt("total_pages"); currentPage = 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) { // 请求失败时重置参数,避免后续空指针 totalResults = 0; totalPages = 0; currentPage = 0; } }); // 返回封装好的完整结果 return new MovieSearchResult(movies, totalResults, totalPages, currentPage); } }
步骤3:修改MainActivity的Loader回调实现
把LoaderCallbacks的泛型改成MovieSearchResult,并在onLoadFinished中获取总结果数更新TextView:
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<MovieSearchResult> { MovieListAdapter adapter; ListView movielist; EditText etKeyword; Button btSearch; TextView tvFound; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new MovieListAdapter(this); adapter.notifyDataSetChanged(); movielist = findViewById(R.id.lvMovie); movielist.setAdapter(adapter); etKeyword = findViewById(R.id.etKeyword); btSearch = findViewById(R.id.btSearch); tvFound = findViewById(R.id.tvFound); 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); } }); // 初始化加载(如果需要默认搜索) String initialKeyword = etKeyword.getText().toString(); Bundle initialBundle = new Bundle(); initialBundle.putString("keyword", initialKeyword); getLoaderManager().restartLoader(0, initialBundle, this); } @Override public Loader<MovieSearchResult> onCreateLoader(int i, Bundle bundle) { String keyword = ""; if( bundle != null) { keyword = bundle.getString("keyword"); } return new MovieAsyncTaskLoader(this, keyword ); } @Override public void onLoadFinished(Loader<MovieSearchResult> loader, MovieSearchResult result) { // 更新电影列表 adapter.setDatamovie(result.getMovies()); // 更新总结果数到TextView tvFound.setText("Found " + result.getTotalResults() + " movies"); // 如果需要实现分页按钮,这里也可以拿到totalPages和currentPage做逻辑处理 } @Override public void onLoaderReset(Loader<MovieSearchResult> loader) { adapter.setDatamovie(null); tvFound.setText(""); // 重置文本 } }
额外说明
- 这种封装方式扩展性很强,后续如果需要传递更多API返回的参数,只需要在
MovieSearchResult中添加对应字段即可。 - 在请求失败的回调中初始化分页参数,能有效避免空指针异常。
- 现在你不仅能显示总结果数,还可以利用
totalPages和currentPage快速实现分页切换功能(比如上一页/下一页按钮)。
内容的提问来源于stack exchange,提问作者Sofyan Thayf
相关产品推荐
相关产品推荐

