RecyclerView in Fragment crashes when navigating to another Fragment

 @Jeel and @Birju showed you the right way to use fragment, but I still leave my answer in case you want to understand deeper why your implementation doesn't work.

Reason:

First, looking into main_layout:

<ConstraintLayout>

    <fragment class="package.RecyclerFragment"
              android:id="@+id/fragment"
     ... />

</ConstraintLayout>

When main_layout is inflated in MainActivity, <fragment> element is simply replaced by whatever included inside RecyclerFragment's layout a.k.a recycler_list layout.

So main_layout will actually become:

<ConstraintLayout>

    <android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/list"
        android:orientation="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</ConstraintLayout>

If you put these code in onResume() in MainActivity, you can see it clearly:

    override fun onResume() {
        super.onResume()
        val parent = findViewById<ConstraintLayout>(R.id.constraint) 
        val numChild = parent.childCount
        val childView = parent.getChildAt(0)
        Log.d("Parent", parent.toString())
        Log.d("NumChild", numChild.toString())
        Log.d("ChildView", childView.toString())
        return
    }

    // Log
    D/Parent: androidx.constraintlayout.widget.ConstraintLayout{a6e5545 V.E...... ......I. 0,0-0,0 #7f07004d app:id/constraint}
    D/NumChild: 1
    D/ChildView: androidx.recyclerview.widget.RecyclerView{753849a VFED..... ......I. 0,0-0,0 #7f070060 app:id/fragment}

Therefore, when you call this line:

fragmentManager?.beginTransaction()?.replace(R.id.fragment, HelloFragment())?.commit()

It actually took RecyclerView as the container view group and add whatever in HelloFragment's layout into RecyclerView

For evidence, you can take a look at these lines in FragmentManager class:


// mContainerId here is R.id.fragment in your layout

container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);

// Now container is RecyclerView

...

f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState)

// After this line, f.mView is the view inside hello.xml

...

container.addView(f.mView);

// Add layout in hello.xml into RecyclerView

Because RecyclerView is designed to hold ViewHolders created from data in Adapter, it still keep a variable called childCount (= 3 in this case) even after Hello's fragment view is added inside RecyclerView and removed all ViewHolders from it.

When new view added, RecyclerView dispatch new layout, which then call a function named findMinMaxChildLayoutPositions()

private void findMinMaxChildLayoutPositions(int[] into) {

        final int count = mChildHelper.getChildCount();
        ...
        for (int i = 0; i < count; ++i) {
            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            if (holder.shouldIgnore()) {
                continue;
            }

As you can see, because all ViewHolders have been removed, holder will be null and NPE will be thrown when it comes to line if (holder.shouldIgnore()) {

Thank you for reading this long answer!