0

I have a fragment where I fetch data from an API using AsyncTaskLoader and then inflate two separate RecyclerView each using custom Adapter. The problem is that when I click on the recycler view to go into the detailsActivity for that particular position then on coming back to the original Activity, I find that the saveInstanceState is empty. But if I do rotate my device the it works pretty well. I don't think that it is the correct behavior. If it works with Screen Rotation then it should work in my troubled case too.

Here's the code for my fragment:

public class MoviePosterFragment extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<Movie>> {

private static final int POPULAR_MOVIE_LOADER_ID = 1111;
private static final int UPCOMING_MOVIE_LOADER_ID = 9999;
ArrayList<Movie> popularMovies;
ArrayList<Movie> upcomingMovies;
PopularMoviesAdapter mPopularMoviesAdapter;
UpcomingMovieAdapter mUpcomingMovieAdapter;
RecyclerView mPopularMovieRecyclerView;
RecyclerView mUpcomingMovieRecyclerView;
Uri.Builder uriBuilder;
String savedInstance;
LinearLayoutManager layoutManagerPopularMoviesPoster;
LinearLayoutManager layoutManagerUpcomingMoviesPoster;

public MoviePosterFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.v("############******", "onCreate called");
    if (savedInstanceState == null) {
        popularMovies = new ArrayList<>();
        upcomingMovies = new ArrayList<>();
        savedInstance = "empty";
        Log.v("############******", "onCreate savedInstance is " + savedInstance);
        //First of all check if network is connected or not then only start the loader
        ConnectivityManager connMgr = (ConnectivityManager)
                getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            /*
             *fetch data. Get a reference to the LoaderManager, in order to interact with loaders.
            */
            Log.v("############******", "startPopularMoviesLoaderManager called");
            startPopularMoviesLoaderManager();
            Log.v("############******", "startUpcomingMoviesLoaderManager called");
            startUpcomingMoviesLoaderManager();

        }

    } else {
        savedInstance = "not empty";
        Log.v("############******", "onCreate savedInstance is " + savedInstance);
        popularMovies = savedInstanceState.getParcelableArrayList("popularMovies");
        upcomingMovies = savedInstanceState.getParcelableArrayList("upcomingMovies");

    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    Log.v("############******", "onCreateView savedInstance is " + savedInstanceState);
    // Inflate the layout for this fragment
    View rootView = inflater.inflate(R.layout.fragment_movie_posters, container, false);

    /* Code referenced from the @link:
     * "https://guides.codepath.com/android/using-the-recyclerview"
     */
    /*
     * Lookup the recyclerView in activity layout
     */
    mPopularMovieRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewPopularMovies);
    mUpcomingMovieRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewTopMoviesMovies);

     /*
      * Setup layout manager for items with orientation
      * Also supports `LinearLayoutManager.HORIZONTAL`
      */
    layoutManagerPopularMoviesPoster = new LinearLayoutManager(getActivity(),
            LinearLayoutManager.HORIZONTAL, false);
        /* Optionally customize the position you want to default scroll to */
    layoutManagerPopularMoviesPoster.scrollToPosition(0);
        /* Attach layout manager to the RecyclerView */
    mPopularMovieRecyclerView.setLayoutManager(layoutManagerPopularMoviesPoster);

     /*
      * Setup layout manager for items with orientation
      * Also supports `LinearLayoutManager.HORIZONTAL`
      */
    layoutManagerUpcomingMoviesPoster = new LinearLayoutManager(getActivity(),
            LinearLayoutManager.HORIZONTAL, false);
        /* Optionally customize the position you want to default scroll to */
    layoutManagerUpcomingMoviesPoster.scrollToPosition(0);
        /* Attach layout manager to the RecyclerView */
    mUpcomingMovieRecyclerView.setLayoutManager(layoutManagerUpcomingMoviesPoster);

    SnapHelper snapHelperForPopularMovieRecyclerView = new GravitySnapHelper(Gravity.START);
    snapHelperForPopularMovieRecyclerView.attachToRecyclerView(mPopularMovieRecyclerView);

    SnapHelper snapHelperForUpcomingMovieRecyclerView = new GravitySnapHelper(Gravity.START);
    snapHelperForUpcomingMovieRecyclerView.attachToRecyclerView(mUpcomingMovieRecyclerView);

    /* Code referenced from the @link:
    * "https://guides.codepath.com/android/using-the-recyclerview"
    */

    // Create mPopularMoviesAdapter passing in the sample user data
    mPopularMoviesAdapter = new PopularMoviesAdapter(getActivity(), popularMovies);
    mPopularMoviesAdapter.setMovieData(popularMovies);
    // Attach the mPopularMoviesAdapter to the recyclerview to populate items
    mPopularMovieRecyclerView.setAdapter(mPopularMoviesAdapter);

    // Create mUpcomingMoviesAdapter passing in the sample user data
    mUpcomingMovieAdapter = new UpcomingMovieAdapter(getActivity(), upcomingMovies);
    mUpcomingMovieAdapter.setMovieData(upcomingMovies);
    // Attach the mUpcomingMoviesAdapter to the recyclerview to populate items
    mUpcomingMovieRecyclerView.setAdapter(mUpcomingMovieAdapter);

    return rootView;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    Log.v("############******", "onActivityCreated called and saveInstance is " + savedInstanceState);
    mPopularMoviesAdapter = new PopularMoviesAdapter(getActivity(), popularMovies);
    mUpcomingMovieAdapter = new UpcomingMovieAdapter(getActivity(), upcomingMovies);
    mUpcomingMovieAdapter.setMovieData(upcomingMovies);
    mPopularMoviesAdapter.setMovieData(popularMovies);
    mPopularMovieRecyclerView.setAdapter(mPopularMoviesAdapter);
    mUpcomingMovieRecyclerView.setAdapter(mUpcomingMovieAdapter);
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.v("############******", "onSaveInstanceState called");
    outState.putParcelableArrayList("popularMovies", popularMovies);
    outState.putParcelableArrayList("upcomingMovies", upcomingMovies);
}


private void startPopularMoviesLoaderManager() {
    LoaderManager loaderManager = getLoaderManager();
    Log.v("############******", "initLoader called with id " + POPULAR_MOVIE_LOADER_ID);
    loaderManager.initLoader(POPULAR_MOVIE_LOADER_ID, null, this);
    Log.v("############******", "startPopularMoviesLoaderManager finished");
}

private void startUpcomingMoviesLoaderManager() {
    LoaderManager loaderManager = getLoaderManager();
    Log.v("############******", "initLoader called with id " + UPCOMING_MOVIE_LOADER_ID);
    loaderManager.initLoader(UPCOMING_MOVIE_LOADER_ID, null, this);
    Log.v("############******", "startUpcomingMoviesLoaderManager finished");
}

@Override
public Loader<ArrayList<Movie>> onCreateLoader(int id, Bundle args) {
    if (id == POPULAR_MOVIE_LOADER_ID) {
        Log.v("############******", "onCreateLoader called with id " + POPULAR_MOVIE_LOADER_ID);
        Uri baseUri = Uri.parse(UrlsAndConstants.MoviePosterQuery.DEFAULT_URL);
        Log.v("############", "baseUri is " + baseUri.toString());
        uriBuilder = baseUri.buildUpon();
        Log.v("############", "uriBuilder is " + uriBuilder.toString());
        uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
        Log.v("############", "uriBuilder.toString() is " + uriBuilder.toString());
        uriBuilder.appendQueryParameter(SORT_BY_KEY, SORT_BY_POPULARITY_VALUE_DESCENDING);

    } else if (id == UPCOMING_MOVIE_LOADER_ID) {
        Log.v("############", "onCreateLoader called with id " + UPCOMING_MOVIE_LOADER_ID);
        Uri baseUri = Uri.parse("https://api.themoviedb.org/3/movie/upcoming");
        Log.v("############", "baseUri is " + baseUri.toString());
        uriBuilder = baseUri.buildUpon();
        Log.v("############", "uriBuilder is " + uriBuilder.toString());
        uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
        Log.v("############", "uriBuilder.toString() is " + uriBuilder.toString());
    }
    return new MoviePosterLoader(getActivity().getApplicationContext(), uriBuilder.toString());
}

@Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> incomingMovieArrayList) {
    switch (loader.getId()) {
        case POPULAR_MOVIE_LOADER_ID:
            Log.v("############******", "onLoadFinished called with id " + POPULAR_MOVIE_LOADER_ID);
            if (incomingMovieArrayList.isEmpty()) {
                Log.v("******************", "popularMovies isEmpty");
                return;
            } else {
                popularMovies = incomingMovieArrayList;
                mPopularMoviesAdapter = new PopularMoviesAdapter(getActivity(), popularMovies);
                mPopularMoviesAdapter.setMovieData(popularMovies);
                mPopularMovieRecyclerView.setAdapter(mPopularMoviesAdapter);

            }
            break;
        case UPCOMING_MOVIE_LOADER_ID:
            Log.v("############******", "onLoadFinished called with id " + UPCOMING_MOVIE_LOADER_ID);
            if (incomingMovieArrayList.isEmpty()) {
                Log.v("******************", "popularMovies isEmpty");
                return;
            } else {
                upcomingMovies = incomingMovieArrayList;
                mUpcomingMovieAdapter = new UpcomingMovieAdapter(getActivity(), upcomingMovies);
                mUpcomingMovieAdapter.setMovieData(upcomingMovies);
                mUpcomingMovieRecyclerView.setAdapter(mUpcomingMovieAdapter);
            }
            break;
    }
}

@Override
public void onLoaderReset(Loader<ArrayList<Movie>> loader) {
    Log.v("############******", "onLoaderReset called ");

}

}

And as you can see below from the Logcat messages, the problem is there for sure.

Logcat messages:

`When app is started for the first time:

12-07 01:37:56.450 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate called
12-07 01:37:56.450 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate savedInstance is empty
12-07 01:37:56.450 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager called
12-07 01:37:56.453 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 1111
12-07 01:37:56.453 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateLoader called with id 1111
12-07 01:37:56.455 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager finished
12-07 01:37:56.455 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager called
12-07 01:37:56.455 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 9999
12-07 01:37:56.455 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager finished
12-07 01:37:56.456 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateView savedInstance is null
12-07 01:37:56.493 9756-9756/me.abhishekraj.showmyshow V/############******: onActivityCreated called and saveInstance is null
12-07 01:37:57.683 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 9999
12-07 01:37:57.731 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 1111
After a popular movie poster is clicked
12-07 01:29:16.062 2256-2256/me.abhishekraj.showmyshow V/############******: onSaveInstanceState called
After coming back from DetailActivity
12-07 01:38:52.785 9756-9756/me.abhishekraj.showmyshow V/############******: onLoaderReset called
12-07 01:38:52.785 9756-9756/me.abhishekraj.showmyshow V/############******: onLoaderReset called
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate called
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate savedInstance is empty
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager called
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 1111
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateLoader called with id 1111
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager finished
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager called
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 9999
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager finished
12-07 01:38:52.851 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateView savedInstance is null
12-07 01:38:52.855 9756-9756/me.abhishekraj.showmyshow V/############******: onActivityCreated called and saveInstance is null
12-07 01:38:53.326 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 9999
12-07 01:38:54.033 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 1111
After a upcoming movie poster is clicked
12-07 01:30:04.572 2256-2256/me.abhishekraj.showmyshow V/############******: onSaveInstanceState called
After coming back from DetailActivity
12-07 01:39:51.672 9756-9756/me.abhishekraj.showmyshow V/############******: onLoaderReset called
12-07 01:39:51.672 9756-9756/me.abhishekraj.showmyshow V/############******: onLoaderReset called
12-07 01:39:51.759 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate called
12-07 01:39:51.759 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate savedInstance is empty
12-07 01:39:51.759 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager called
12-07 01:39:51.759 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 1111
12-07 01:39:51.759 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateLoader called with id 1111
12-07 01:39:51.760 9756-9756/me.abhishekraj.showmyshow V/############******: startPopularMoviesLoaderManager finished
12-07 01:39:51.760 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager called
12-07 01:39:51.760 9756-9756/me.abhishekraj.showmyshow V/############******: initLoader called with id 9999
12-07 01:39:51.760 9756-9756/me.abhishekraj.showmyshow V/############******: startUpcomingMoviesLoaderManager finished
12-07 01:39:51.761 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateView savedInstance is null
12-07 01:39:51.765 9756-9756/me.abhishekraj.showmyshow V/############******: onActivityCreated called and saveInstance is null
12-07 01:39:51.815 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 1111
12-07 01:39:51.817 9756-9756/me.abhishekraj.showmyshow V/############******: onLoadFinished called with id 9999
on rotate
12-07 01:40:18.441 9756-9756/me.abhishekraj.showmyshow V/############******: onSaveInstanceState called
12-07 01:40:18.472 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate called
12-07 01:40:18.472 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate savedInstance is not empty
12-07 01:40:18.478 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateView savedInstance is Bundle[{upcomingMovies=[me.abhishekraj.showmyshow.Movie@c837a9f, me.abhishekraj.showmyshow.Movie@42b07b5, me.abhishekraj.showmyshow.Movie@da99e4a,.......and so on..that means it's not empty
onRotating back
12-07 01:40:48.244 9756-9756/me.abhishekraj.showmyshow V/############******: onSaveInstanceState called
12-07 01:40:48.281 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate called
12-07 01:40:48.281 9756-9756/me.abhishekraj.showmyshow V/############******: onCreate savedInstance is not empty
12-07 01:40:48.286 9756-9756/me.abhishekraj.showmyshow V/############******: onCreateView savedInstance is Bundle[{upcomingMovies=[me.abhishekraj.showmyshow.Movie@c837a9f, me.abhishekraj.showmyshow.Movie@42b07b5, me.abhishekraj.showmyshow.Movie@da99e4a, me.abhishekraj.showmyshow.Movie@67a86d8, me.abhishekraj.showmyshow.Movie@a516a31, me.abhishekraj.showmyshow.Movie@e241e97,.....and so on...that means it's not empty`

Where's the problem then? Why do I get blank saveInstanceState when I return from another activity to this fragment but not-null saveInstanceState for screen rotation, even when activity is created again in both cases(???). Thanks!

Edit:

If more of my code is needed to take the reference then it can be found in the following: Gist

Abhishek Raj
  • 25
  • 2
  • 15

1 Answers1

0

The problem can be handled quite easily if use launch mode for main activity as single-top which seems totally acceptable in my use-case. Here's what my manifest.xml looks like after resolving the issue:

    <activity
        android:name=".MainActivity"
        android:launchMode="singleTop">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

But, a much better approach can be by using Back Stack so that activity state can be saved before it being destroyed. I'll update the answer as and when I solve it using back-stack but for now it does the work.

Abhishek Raj
  • 25
  • 2
  • 15