当我回到 MainActivity 时,AsyncTaskLoader 不断重新加载数据

AsyncTaskLoader keeps reloading data when I come back to the MainActivity

我对 Android 开发还很陌生,我真的需要你的帮助。我的问题在下面的 MainActivity 中。该应用程序主要在 activity 中显示电影列表,在另一个 activity 中显示电影详细信息。问题在于,每当用户从 MovieActivity 返回到 MainActivity 时,加载程序会再次开始加载数据,尽管电影已经存在。然后它无法停止加载数据。这真的很烦人。我想摆脱这个。因此,当用户返回到 MainActivity 时,加载程序将知道已经加载了数据并且不会加载任何内容 again.If 它有帮助,这是我的完整 GitHub repohttps://github.com/mateuszwojnarowicz/PopularMovies

我被困了大约 3 周,尝试了数百种可能的解决方案。似乎没有任何效果。我真的很绝望。

非常感谢您的帮助,

马修

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

private MovieAdapter mAdapter;
private ArrayList<Movie> mMoviesCollection;
private SharedPreferences sharedPreferences;
private Resources resources;
private LoaderManager loaderManager;
private Loader<String> loader;
private RecyclerView.LayoutManager layoutManager;
private String sortBy;

@BindView(R.id.pb)
ProgressBar progressBar;
@BindView(R.id.er)
TextView errorTextView;
@BindView(R.id.rv)
RecyclerView recyclerView;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    mMoviesCollection = new ArrayList<Movie>();
    sharedPreferences = getSharedPreferences(Constants.SHARED_PREFS, Activity.MODE_PRIVATE);
    resources = getResources();
    sortBy = sharedPreferences.getString(Constants.KEY_SORT, null);
    setSharedPref();

    layoutManager = new GridLayoutManager(this, calculateNoOfColumns(this));
    loaderManager = getLoaderManager();
    loader = loaderManager.getLoader(Constants.LOADER_MOVIES_ID);
    initialize();
    makeOperationLoadMovies(sortBy);

}

public static int calculateNoOfColumns(Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
    float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
    int noOfColumns = (int) (dpWidth / 150);
    return noOfColumns;
}


//Set first-launch pref and set title according to pref
private void setSharedPref(){
    if(!sharedPreferences.contains(Constants.KEY_SORT)) {
        saveData(Constants.VALUE_POP);
        setTitle(resources.getString(R.string.title_pop));
    } else {
        if (Objects.equals(sharedPreferences.getString(Constants.KEY_SORT, null), Constants.VALUE_POP)) {
            setTitle(resources.getString(R.string.title_pop));
        }
        if (Objects.equals(sharedPreferences.getString(Constants.KEY_SORT, null), Constants.VALUE_TOP)) {
            setTitle(resources.getString(R.string.title_top));
        }
    }
}

//Set up the RecyclerView
private void initialize(){
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setHasFixedSize(true);
    mMoviesCollection = new ArrayList<>();
    mAdapter = new MovieAdapter(mMoviesCollection, this, this);
    recyclerView.setAdapter(mAdapter);
}

private void makeOperationLoadMovies(String SORT_BY){
    Bundle bundle = new Bundle();
    bundle.putString(Constants.LOADER_MOVIES_EXTRA, SORT_BY);
    if(recyclerView.isDirty()){

    }
    else if(loader==null){
        loaderManager.initLoader(Constants.LOADER_MOVIES_ID, bundle, this);
    }else{
        loaderManager.restartLoader(Constants.LOADER_MOVIES_ID, bundle, this);
    }

}

//Update shared pref
private void saveData(String SORT_VALUE){
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putString(Constants.KEY_SORT, SORT_VALUE);
    editor.apply();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    switch (id){
        case R.id.menu_fav:
            startActivity(new Intent(MainActivity.this, FavoritesActivity.class));
            break;
        case R.id.menu_pop:
            saveData(Constants.VALUE_POP);
            Toast.makeText(this, resources.getString(R.string.message_popularity),Toast.LENGTH_LONG).show();
            break;
        case R.id.menu_top:
            saveData(Constants.VALUE_TOP);
            Toast.makeText(this, resources.getString(R.string.message_rating),Toast.LENGTH_LONG).show();
            break;
    }

    return super.onOptionsItemSelected(item);
}

@Override
protected void onPause() {
    super.onPause();
    Parcelable recyclerViewState;
    recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();//save
    recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
}

@Override
protected void onPostResume() {
    super.onPostResume();
    Parcelable recyclerViewState;
    recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();//save
    recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);

}

@SuppressLint("StaticFieldLeak")
@Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
    return new AsyncTaskLoader<String>(this) {


        @Override
        protected void onStartLoading() {
            super.onStartLoading();
                forceLoad();
                progressBar.setVisibility(View.VISIBLE);
                errorTextView.setVisibility(View.INVISIBLE);

        }

        @Override
        public void deliverResult(String data) {
            super.deliverResult(data);
        }

        @Override
        public String loadInBackground() {



            String jsonString = "";
            URL url = NetworkUtils.buildUrl(args.getString(Constants.LOADER_MOVIES_EXTRA));
            try {
                jsonString += NetworkUtils.getResponseFromHttpUrl(url);
            } catch (IOException e) {
                e.printStackTrace();
            }


            if(jsonString.isEmpty()){


            } else {

                try {
                    JSONObject jsonObject = new JSONObject(jsonString);
                    JSONArray jsonArray = jsonObject.getJSONArray(Constants.JSON_KEY_MOVIE_RESULTS);
                    for (int i = 0; i < jsonArray.length(); i++) {
                        //Get 1 movie from JSON
                        String mTitle;
                        int mId;
                        String mPosterUrl;
                        String mPlot;
                        double mUserRating;
                        String mReleaseDate;

                        JSONObject Jmovie = (JSONObject) jsonArray.get(i);
                        mTitle = Jmovie.getString(Constants.JSON_KEY_MOVIE_TITLE);
                        mId = Jmovie.getInt(Constants.JSON_KEY_MOVIE_ID);
                        mPosterUrl = NetworkUtils.getPosterString(Jmovie.getString(Constants.JSON_KEY_MOVIE_POSTER_PATH));
                        mPlot = Jmovie.getString(Constants.JSON_KEY_MOVIE_OVERVIEW);
                        mUserRating = Jmovie.getDouble(Constants.JSON_KEY_MOVIE_VOTE_AVERAGE);
                        mReleaseDate = Jmovie.getString(Constants.JSON_KEY_MOVIE_RELEASE_DATE);
                        //Get videos
                        ArrayList<Video> mVideos = new ArrayList<Video>();
                        URL videosURL = NetworkUtils.buildUrlVideos(String.valueOf(mId));
                        String videosJSON = NetworkUtils.getResponseFromHttpUrl(videosURL);
                        JSONObject jsonObjectVideos = new JSONObject(videosJSON);
                        JSONArray jsonArrayVideos = jsonObjectVideos.getJSONArray(Constants.JSON_KEY_VIDEO_RESULTS);
                        if(jsonArrayVideos.length()==0){
                            mVideos = null;
                        } else {
                            for(int v = 0; v < jsonArrayVideos.length(); v++){
                                JSONObject Jvideo = (JSONObject) jsonArrayVideos.get(v);
                                String mVideoName;
                                String mVideoUrlString;
                                mVideoName = Jvideo.getString(Constants.JSON_KEY_VIDEO_NAME);
                                mVideoUrlString = "https://www.youtube.com/watch?v="+Jvideo.getString(Constants.JSON_KEY_VIDEO_KEY);
                                Video video = new Video(mVideoName, mVideoUrlString);
                                mVideos.add(video);
                            }
                        }
                        //GetReviews
                        ArrayList<Review> mReviews = new ArrayList<Review>();
                        URL reviewsURL = NetworkUtils.buildUrlReviews(String.valueOf(mId));
                        String reviewsJSON = NetworkUtils.getResponseFromHttpUrl(reviewsURL);
                        JSONObject jsonObjectReviews = new JSONObject(reviewsJSON);
                        JSONArray jsonArrayReviews = jsonObjectReviews.getJSONArray(Constants.JSON_KEY_REVIEW_RESULTS);
                        if(jsonArrayReviews.length()!=0) {
                            for(int r = 0; r < jsonArrayReviews.length(); r++){
                                JSONObject Jreview = (JSONObject) jsonArrayReviews.get(r);
                                String mReviewName;
                                String mReviewText;
                                mReviewName = Jreview.getString(Constants.JSON_KEY_REVIEW_AUTHOR);
                                mReviewText = Jreview.getString(Constants.JSON_KEY_REVIEW_CONTENT);
                                Review review = new Review(mReviewName, mReviewText);
                                mReviews.add(review);
                            }
                        }
                        Movie movie = new Movie(mTitle, mId, mPosterUrl, mPlot, mUserRating, mReleaseDate, mVideos, mReviews);
                        mMoviesCollection.add(movie);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
            return null;
        }


    };
}

@Override
public void onLoadFinished(Loader<String> loader, String data) {

    progressBar.setVisibility(View.GONE);

    mAdapter.notifyDataSetChanged();
}

@Override
public void onLoaderReset(Loader<String> loader) {

}
}

因为您是Android的新手,所以有很多错误。所以,很多人可能不想插话。无论如何,我也是新人,而且现在和你一样class,所以我会试一试。

首先,您的加载器没有return输入正确的数据类型。你的装载机应该是 Loader<List<Movie>> 并且它应该是 return 和 new AsyncTaskLoader<List<Movie>>。您想要这样做的原因是要利用 AsyncTaskLoader 必须提供的一切。我会进一步解释。

其次,我们将通过将初始引用从 Activity 移动到加载程序来缓存加载程序中的数据。

所以移动 private ArrayList<Movie> mMoviesCollection; 作为你的 AsyncTaskLoader 的一个实例变量。从 onCreate 和 initialize 方法中删除行 mMoviesCollection = new ArrayList<Movie>();

在你的AsyncTaskLoader中,你需要在forceLoad和实现deliverResult之前检查你的数据是否已经存在于你的onStartLoading中。

因此,您的 onStartLoading() 应该如下所示:

@Override
    protected void onStartLoading() {
        super.onStartLoading();
            if(mMoviesCollection.isEmpty()){
                forceLoad();
                progressBar.setVisibility(View.VISIBLE);
                errorTextView.setVisibility(View.INVISIBLE);
            } else {
                deliverResult(mMoviesCollection)
            }
    }

你的 deliverResult 应该是这样的:

@Override
    public void deliverResult(List<Movie> data) {
        mMoviesCollection = data;
        super.deliverResult(data);
    }

现在您需要实现一个 setData(List<Movie> movies) 方法来设置您的适配器的数据实例变量并在您的适配器中调用 notifyDataSetChanged()。像这样:

public void setData(List<Movie> movies){
    mMovies = movies;
    notifyDataSetChanged();
}

从适配器的构造函数中删除 List<Movie>。这样你就可以在没有任何数据的情况下构建适配器。如果数据为空,适配器的 getItemCount() 应该 return 0 并且 recyclerView 不会尝试构建视图。

完成后,您可以像这样调用 onLoadFinished:

@Override
    public void onLoadFinished(Loader<List<Movie>> loader, List<Movie> data) {
        progressBar.setVisibility(View.GONE);
        mAdapter.setData(data);
    }

编辑: 进行更正以说明 ArrayList 实例化为实例变量。你可以不在那里实例化 mMoviesCollection 然后稍后再做,或者只是用 mMoviesCollection.isEmpty() 检查它是否为空,因为我在 onStartLoading 上面改变了。:

编辑: 您需要弄清楚您的图书馆,您在某些地方使用 android.app 而在其他地方使用 android.support。

因此在您的导入中更改这些:

import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Loader;

全部到:

import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;

现在 TMDB.org API 的请求限制为每 10 秒 40 个请求。 https://developers.themoviedb.org/3/getting-started/request-rate-limiting

因此,您的 Loader 甚至没有完成所有操作并抛出异常。我建议在将视频和评论调用到 MovieActivity 时,通过在其中创建另一个 AsyncTaskLoader 并在详细信息屏幕加载时调用每个 AsyncTaskLoader 来分手。

从技术上讲,您也可以在 AsyncTaskLoader 中添加 Thread.sleep(300) 或更少,但这会使它变得非常慢。换句话说,您必须将数据推送到超过 10 秒标记才能完全加载。

现在,有了这些以及我们所做的更改,一切都会在配置更改(例如屏幕旋转)后继续存在。

如果您希望数据继续存在,您将不得不以某种方式持久化数据。就像将 json 响应保存为 onSaveInstanceState 中的字符串或将 JSon 字符串保存到您创建的数据库中。