Andrew Butler Andrew Butler - 15 days ago 6
Java Question

Why am I getting a null reference on my RecyclerView

I am trying to use the RecyclerView in my fragments. It shows up fine for the first tab, but when I swipe to the second tab and then back to the first one I get the following error:


java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.support.v7.widget.RecyclerView$LayoutManager.stopSmoothScroller()'
on a null object reference


Does anyone know why I would be getting this error? It seems that there is a call that I'm missing before I the fragments switch, but I can't figure it out.

Pager Adapter for Fragments:

public class PagerAdapter extends FragmentPagerAdapter {

private final String[] TITLES = {"Header 0", "Header 1" };

public PagerAdapter(FragmentManager fm){
super(fm);
}

@Override
public CharSequence getPageTitle(int position){
return TITLES[position];
}

@Override
public int getCount() {
return TITLES.length;
}

@Override
public Fragment getItem(int position){
switch(position){
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
default:
return new FragmentOne();
}
}
}


FragmentOne & FragmentTwo use a boilerplate class I created:

public class FragmentOne extends Base {
... code for displaying content in the RecyclerView. Just contains my custom Adapter for the RecyclerView ...
}


Base:

public abstract class Base extends Fragment {

public View mView;
public RecyclerView mDataView;
public ProgressBar mProgressBar;
public Context mContext;
private RecyclerView.LayoutManager mLayoutManager;

public Base() { }

@Override
public View onCreateView(LayoutInflater _i, ViewGroup _vg, Bundle savedInstanceBundle){
mView = _i.inflate(R.layout.f_base, null);
mDataView = (RecyclerView) mView.findViewById(R.id.data);
mProgressBar = (ProgressBar)mView.findViewById(R.id.loading);
mLayoutManager = new LinearLayoutManager(mContext);
mDataView.setLayoutManager(mLayoutManager);
mContext = this.getActivity();
return mView;
}

@Override
public void onStart() {
super.onStart();
init();
doWork();
}

public abstract void init();

public abstract void doWork();

}


EDIT

Error Stack Trace:

11-02 12:49:44.171 3708-3708/com.nitrox.digitune E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.nitrox.digitune, PID: 3708
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView$LayoutManager.stopSmoothScroller()' on a null object reference
at android.support.v7.widget.RecyclerView.stopScrollersInternal(RecyclerView.java:1159)
at android.support.v7.widget.RecyclerView.stopScroll(RecyclerView.java:1151)
at android.support.v7.widget.RecyclerView.onDetachedFromWindow(RecyclerView.java:1356)
at android.view.View.dispatchDetachedFromWindow(View.java:13441)
at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2837)
at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2834)
at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2834)
at android.view.ViewGroup.removeViewInternal(ViewGroup.java:4163)
at android.view.ViewGroup.removeViewInternal(ViewGroup.java:4136)
at android.view.ViewGroup.removeView(ViewGroup.java:4068)
at android.support.v4.view.ViewPager.removeView(ViewPager.java:1326)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1045)
at android.support.v4.app.FragmentManagerImpl.detachFragment(FragmentManager.java:1280)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:724)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1489)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:486)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1073)
at android.support.v4.view.ViewPager.populate(ViewPager.java:919)
at android.support.v4.view.ViewPager$3.run(ViewPager.java:249)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:549)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Answer

Basically, your LayoutManager is being disposed of before your recycler has finished with it.

From the Android source :

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (mItemAnimator != null) {
        mItemAnimator.endAnimations();
    }
    mFirstLayoutComplete = false;
    stopScroll();
    mIsAttached = false;
    if (mLayout != null) {
        mLayout.onDetachedFromWindow(this, mRecycler);
    }
    removeCallbacks(mItemAnimatorRunner);
}

The problem is stopScroll tries to call mLayout.stopSmoothScroller(); without checking if mLayout is null. I threw togeather a very hacky hot fix for an app I've been working on but should NOT be used as a long term solution as it's very much a hack, but I had a tight deadline so I just had to catch the null pointer exception. I cut out my handler because it was application specific.

My hot fix was just to create a custom View extending the RecyclerView :

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;

public class HotFixRecyclerView extends RecyclerView
{
    public HotFixRecyclerView( Context context )
    {
        super( context );
    }

    public HotFixRecyclerView( Context context, AttributeSet attrs )
    {
        super( context, attrs );
    }

    public HotFixRecyclerView( Context context, AttributeSet attrs, int defStyle )
    {
        super( context, attrs, defStyle );
    }

    @Override
    public void stopScroll()
    {
        try
        {
            super.stopScroll();
        }
        catch( NullPointerException exception )
        {
            /**
             *  The mLayout has been disposed of before the 
             *  RecyclerView and this stops the application 
             *  from crashing.
             */
        }
    }
}

Then change all references to the RecyclerView to my own View. If you do use it please remove it once Android have patched this issue as it's a bit of a hack.