Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 899
Andrew Butler Avatar asked Nov 02 '14 18:11

Andrew Butler


4 Answers

Basically, the 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 stems from when the stopScroll and it 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 I wouldn't suggest using this for a long term solution as it's very much a bodge. If like me you have a tight deadline it's best to just catch the null pointer exception and ignore it.

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 from the RecyclerView to HotFixRecyclerView. If you do use it please remove it once Android have patched this issue as it's a bit of a hack.

  • If you use recylerview inn XML don't forget to change your XML files accordingly to use com.your.package.HotFixRecyclerView instead of android.support.v7.widget.RecyclerView
like image 160
James Lockhart Avatar answered Nov 16 '22 05:11

James Lockhart


It seems that If you have a RecyclerView in your layout and you load it in the Activity, it is a must to set a LayoutManager for it, else it will throw this Exception when trying to destroy it. I have just experienced this error and this is the way I've solved it:

My activity showed elements in a pair of TextViews or in a RecyclerView depending on the amount of items: if it was a collection of items, I used the RecyclerView; if there was only one item, it was displayed on the TextViews and I set the RecyclerView's visibility to GONE.

Then, in the first case, I called myRecyclerView.setLayoutManager(myLayoutManager), but in the other case I didn't, for I wasn't going to use it. In both cases, the Activity containing the RecyclerView was displayed perfectly. But when closing it, the Exception was thrown in the second case because the RecyclerView, though not used, existed and when trying to dispose of it, it seems it couldn't. So I just assigned the LayoutManager in both cases although I wasn't using it, and this solved the issue.

Here's the code from my Activity:

ItemsAdapter adapter;
RecyclerView myRecyclerView = findViewById(R.id.my_recycler_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);

if (items.size() > 1){
    adapter = new ItemsAdapter(this, R.layout.item_layout, items);
    myRecyclerView.setLayoutManager(layoutManager);
    myRecyclerView.setAdapter(adapter);
} else {
    tv_field1.setText(items.get(0).getField1());
    tv_field2.setText(items,get(0).getField2());
    myRecyclerView.setVisibility(View.GONE);

    //This is what I had to add
    myRecyclerView.setLayoutManager(layoutManager);
}
like image 37
Juan José Melero Gómez Avatar answered Nov 16 '22 05:11

Juan José Melero Gómez


Reading the bug posted by Isaak, the best solution seems to be assigning the Layout Manager as soon as the view is inflated.

In my case, I only had to assign it on onCreate to fix the problem:

mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
like image 3
Felizardo Avatar answered Nov 16 '22 06:11

Felizardo


Because it has already inflated pref.xml . You should remove this:

    @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    mRootView = inflater.inflate(R.layout.xxx, container, false);
    return mRootView;
}
like image 3
Huy Nguyen Avatar answered Nov 16 '22 06:11

Huy Nguyen