0

My app is a master/detail flow based app that fetches pictures from reddit, displays them in a recyclerView, and upon clicking opens a detail view where you can set the picture as a wallpaper. I can't figure out how to get phone rotation to work properly. My recyclerView is returning null in my onViewStateRestored method when the phone is rotated and is crashing my app.

I've looked through various tutorials and examples, and lots of stack overflow questions, but can't find a specific answer.

Here is the fragment code I'm dealing with:


    private DataAdapter adapter;
    private RecyclerView recyclerView;
    private OnFragmentInteractionListener mListener;
    private static final String BUNDLE_RECYCLER_LAYOUT = "ListFragment.recycler.layout";
    public View myView;

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

    public static ListFragment newInstance() {
        ListFragment fragment = new ListFragment();
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            GetDataService service = RetrofitInstance.getRetrofitInstance().create(GetDataService.class);

            Call<Article> call = service.getData();

            Log.wtf("Url Called", call.request().url() + "");

            call.enqueue(new Callback<Article>() {
                @Override
                public void onResponse(Call<Article> call, Response<Article> response) {
                    Log.wtf("Response body", response.body().getData().getChildren().toString());
                    generateDataList(response.body().getData().getChildren());
                }

                @Override
                public void onFailure(Call<Article> call, Throwable t) {
                    Toast.makeText(getActivity(), "Something went wrong...Please try again later!", Toast.LENGTH_LONG).show();
                    t.printStackTrace();
                }
            });
        }
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        if(myView == null) {
            myView = inflater.inflate(R.layout.fragment_list, container, false);
        }
        // Inflate the layout for this fragment
        return myView;

    }

    private void generateDataList(List<Child> dataList) {
        recyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view_data_list);

        dataList.subList(0, 2).clear();

        adapter = new DataAdapter(getActivity(), dataList);

        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());

        recyclerView.setHasFixedSize(true);

        recyclerView.setLayoutManager(layoutManager);

        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);

        if(savedInstanceState != null) {
                Parcelable savedRecyclerInstanceState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
                recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerInstanceState);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

And here is the stack trace I get when I rotate the phone:

    Process: com.botsone.amoleddit, PID: 5097
    java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView$LayoutManager.onRestoreInstanceState(android.os.Parcelable)' on a null object reference
        at com.botsone.amoleddit.fragment.ListFragment.onViewStateRestored(ListFragment.java:129)
        at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:543)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2656)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2610)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1470)
        at android.app.Activity.performStart(Activity.java:7184)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3147)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1955)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:7029)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
JP Wile
  • 156
  • 3
  • 11
  • `ListFragment.java:129` where is line 129 of ListFragment? – Code-Apprentice Aug 13 '19 at 22:39
  • 1
    It appears to be `recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerInstanceState);`. The error is happening because `getLayoutManager()` returns null. Why are you calling `onRestoreInstanceState()` of the layout manager? – Code-Apprentice Aug 13 '19 at 22:40
  • I was using this answer to save the recyclerView state so that when I return to this fragment after going to the detail fragment, it would stay in the same place in the list: https://stackoverflow.com/a/29166336/537155 – JP Wile Aug 13 '19 at 23:11
  • 1
    `getLayoutManager()` returns null because you haven't set it yet. You are setting it in `generateDataList()` which is called from `onResponse()` in an anonymous `Callback
    `. This appears to be asyncronous to the `onCreate()` lifecycle method and hasn't executed before `onViewStateRestored()`. You need to rethink how this all goes together.
    – Code-Apprentice Aug 13 '19 at 23:30

0 Answers0