No indication as to where NPE could occur

2019-06-08 06:03发布

问题:

This is the stack-trace that I get as an error:

Process: com.peems.itcrowd, PID: 2949 java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:873) at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:215) at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1481) at android.view.View.dispatchRestoreInstanceState(View.java:15751) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3231) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3237) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3237) at android.view.View.restoreHierarchyState(View.java:15729) at android.support.v4.app.Fragment.restoreViewState(Fragment.java:475) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1329) at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1528) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1595) at android.support.v4.app.BackStackRecord.executePopOps(BackStackRecord.java:803) at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2353) at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2146) at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2098) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2008) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:710) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

I will explain how I get to this error. In my fragment I call this method which will then go through my activity and fire up another fragment which contains a viewpager.

@Override
public void checkoutCartResult(Result<Shop> result) {
    mShop = result.getData();
    sendActionToActivity(ACTION_ORDER, mShop);
    DisplayUtil.doToast(getContext(), getString(R.string.checkout_successful));
}

private void sendActionToActivity(String action, Shop shop) {
    if (mListener == null) {
        return;
    }
    Bundle bundle = new Bundle();
    bundle.putString(Constants.ACTION_KEY, action);
    bundle.putParcelable(Constants.DATA_KEY_1, Parcels.wrap(shop));
    mListener.onFragmentInteraction(bundle);
}

The above method will fire up this fragment:

    public class ShopFragment extends BaseFragment implements ShopView,
        ViewPager.OnPageChangeListener {

    private static final String TAG = ShopFragment.class.getName();
    private static final String ARG_PARAM1 = "param1";

    FragmentShopBinding mBinder;
    Shop mShop;
    ShopPresenter mPresenter;
    ShopAdapter mAdapter;
    PreferenceAdapter mPreferenceAdapter;
    LayerDrawable mCartMenuIcon;
    List<String> mTabTitles;
    String mImagePath;
    boolean isProductsShown = false;

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

    public static ShopFragment newInstance(Shop shop) {
        ShopFragment fragment = new ShopFragment();
        Bundle args = new Bundle();
        args.putParcelable(ARG_PARAM1, Parcels.wrap(shop));
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        if (getArguments() != null) {
            Parcelable parcelable;
            parcelable = getArguments().getParcelable(ARG_PARAM1);
            mShop = Parcels.unwrap(parcelable);
        }
        mTabTitles = new ArrayList<>(4);
        mTabTitles.add(getString(R.string.wishlist_fragment));
        mTabTitles.add(getString(R.string.history_fragment));
        mTabTitles.add(getString(R.string.offers_fragment));
        mTabTitles.add(getString(R.string.products_fragment));

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mBinder = DataBindingUtil.inflate(inflater, R.layout.fragment_shop, container, false);
        mBinder.tabs.setTabMode(TabLayout.MODE_FIXED);
        mBinder.tabs.setTabGravity(TabLayout.GRAVITY_FILL);
        mAdapter = new ShopAdapter(getChildFragmentManager(), mTabTitles, mShop);
        mPreferenceAdapter = new PreferenceAdapter(getContext());
        mBinder.viewpager.setAdapter(mAdapter);
        mBinder.viewpager.addOnPageChangeListener(this);
        checkForImage();
        mBinder.tabs.setupWithViewPager(mBinder.viewpager);
        mPresenter = new ShopPresenterImpl(this);
        ((MainActivity) getActivity()).setActionBarTitle(mShop.getName());
        EventBus.getDefault().register(this);
        return mBinder.getRoot();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        EventBus.getDefault().unregister(this);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        //show cart items in badge
        mCartMenuIcon = (LayerDrawable) menu.findItem(R.id.action_cart).getIcon();
        setBadgeCount(mCartMenuIcon, String.valueOf(mShop.getCartItems()));

        // show/hide login icon
        if (mShop.getLoginId() == -1) {
            menu.findItem(R.id.action_login).setVisible(true);
            menu.findItem(R.id.action_logout).setVisible(false);
        } else {
            menu.findItem(R.id.action_login).setVisible(false);
            menu.findItem(R.id.action_logout).setVisible(true);
        }
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    protected void setTypeface() {

    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        DisplayUtil.hideKeyboard(ShopFragment.this.getActivity());
    }

    @Override
    public void onPageSelected(int position) {
        String pageTitle;
        pageTitle = mAdapter.getPageTitle(position).toString();
        isProductsShown = pageTitle.equals(getString(R.string.products_fragment));
        ShopFragment.this.getActivity().invalidateOptionsMenu();
        //reloadData(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    @Override
    public void logoutShop(Shop shop) {

    }

    @Override
    public void showError(Error error) {
        DisplayUtil.okDialog(getContext(), error.getErrorTitle(), error.getErrorMessage(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
    }

    @Override
    public void showProgress(boolean show) {

    }

    private void sendActionToActivity(String action) {
        if (mListener == null) {
            return;
        }
        Bundle bundle = new Bundle();
        bundle.putString(Constants.ACTION_KEY, action);
        mListener.onFragmentInteraction(bundle);
    }

    private void logoutShop() {
        AsyncExecutor.create().execute(new AsyncExecutor.RunnableEx() {
            @Override
            public void run() throws Exception {
                mPresenter.logoutShop(mShop);
                ShopRepository shopRepository;
                shopRepository = new ShopRepository();
                mShop.setLoginId(-1);
                mShop.setCustomerId(-1);
                mShop.setCartNumber(0);
                mShop.setLineNumber(0);
                mShop.setCartItems(0);
                shopRepository.updateLoginNumber(mShop);
                shopRepository.updateCart(mShop);
                notifyChanges();
            }
        });
    }

    public void logoutDialog() {
        DisplayUtil.logoutDialog(getContext(), getString(R.string.action_log_out), getString(R.string.are_you_sure),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        logoutShop();
                    }
                }, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
    }

    private void setBadgeCount(LayerDrawable icon, String count) {
        BadgeDrawable badge;
        // Reuse drawable if possible
        Drawable reuse = icon.findDrawableByLayerId(R.id.ic_badge);
        if (reuse != null && reuse instanceof BadgeDrawable) {
            badge = (BadgeDrawable) reuse;
        } else {
            badge = new BadgeDrawable(getContext());
        }
        badge.setCount(count);
        icon.mutate();
        icon.setDrawableByLayerId(R.id.ic_badge, badge);
    }

    private void notifyChanges() {
        AuthShopResult event;
        event = new AuthShopResult();
        event.setShop(mShop);
        EventBus.getDefault().post(event);
    }

    private void checkForImage() {
        mImagePath = mPreferenceAdapter.readImagePathMax();
        if (mImagePath.equals("")) {
            mBinder.viewpager.setOffscreenPageLimit(4);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessage(LoadCartNumber event) {
        mShop = event.getShop();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessage(AuthShopResult event) {
        mShop = event.getShop();
        // hide Login option menu
        if (getActivity() != null) {
            getActivity().invalidateOptionsMenu();
        }
    }
}

This is the activity that is doing all of the transactions.

    public class MainActivity extends AppCompatActivity implements
        BaseFragment.OnFragmentInteractionListener,
        BaseDialogFragment.OnFragmentInteractionListener,
        FragmentManager.OnBackStackChangedListener {

    private static final String CURRENT_FRAGMENT = "current_fragment";
    private static final String CURRENT_PEEM_ID = "current_peem_id";
    private static final String CURRENT_SHOP_ID = "current_shop_id";
    private static final String CURRENT_CART_NUMBER = "current_cart_number";
    private static final String CURRENT_CART_LINE = "current_cart_line";
    private static final String CURRENT_CART_ITEMS = "current_cart_items";

    public ActivityMainBinding mBinder;
    private Peem mCurrentPeem;
    private Shop mCurrentShop;
    private Fragment mFragment;
    private DialogFragment mDialogFragment;
    private PreferenceAdapter mPreferenceAdapter;
    private int mFragmentBackStackCounter = 0;
    private int languagePosition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //LeakCanary.install(getApplication());
        mBinder = DataBindingUtil.setContentView(this, R.layout.activity_main);
        setSupportActionBar(mBinder.toolbar);
        showActionBar(true);
        mPreferenceAdapter = new PreferenceAdapter(App.getContext());
        getSupportFragmentManager().addOnBackStackChangedListener(MainActivity.this);
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        EventBus.getDefault().register(this);
        if (savedInstanceState != null) {
            mFragment = getSupportFragmentManager().getFragment(savedInstanceState, CURRENT_FRAGMENT);
            PeemRepository peemRepository = new PeemRepository();
            ShopRepository shopRepository = new ShopRepository();
            try {
                Peem peem;
                int peemId = savedInstanceState.getInt(CURRENT_PEEM_ID);
                peem = peemRepository.findById(peemId);
                mCurrentPeem = peem;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                Shop shop;
                int shopId = savedInstanceState.getInt(CURRENT_SHOP_ID);
                shop = shopRepository.findById(shopId);
                mCurrentShop = shop;
                if (mCurrentShop != null && mCurrentShop.getLoginId() == -1) {
                    mCurrentShop.setCartNumber(savedInstanceState.getInt(CURRENT_CART_NUMBER));
                    mCurrentShop.setLineNumber(savedInstanceState.getInt(CURRENT_CART_LINE));
                    mCurrentShop.setCartItems(savedInstanceState.getInt(CURRENT_CART_ITEMS));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } else {
            if (isUserLoggedIn()) {
                checkFavourite();
            } else {
                fragmentManager(RegisterFragment.newInstance());
            }
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (getCurrentFragment() == null) {
            getSupportFragmentManager().putFragment(outState, CURRENT_FRAGMENT, mFragment);
        } else {
            mFragment = getCurrentFragment();
            getSupportFragmentManager().putFragment(outState, CURRENT_FRAGMENT, mFragment);
        }

        if (mCurrentPeem != null) {
            int peemId = mCurrentPeem.getId();
            outState.putInt(CURRENT_PEEM_ID, peemId);
        }
        if (mCurrentShop != null) {
            int shopId = mCurrentShop.getId();
            int cartNumber = mCurrentShop.getCartNumber();
            int cartLineNumber = mCurrentShop.getLineNumber();
            int cartItems = mCurrentShop.getCartItems();
            outState.putInt(CURRENT_SHOP_ID, shopId);
            outState.putInt(CURRENT_CART_NUMBER, cartNumber);
            outState.putInt(CURRENT_CART_LINE, cartLineNumber);
            outState.putInt(CURRENT_CART_ITEMS, cartItems);
        }
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getSupportFragmentManager().removeOnBackStackChangedListener(com.peems.itcrowd.ui.activity.MainActivity.this);
        EventBus.getDefault().unregister(this);
        mFragment = null;
        mDialogFragment = null;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_ab, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_back_to_peem:
                mFragment = PeemFragment.newInstance();
                getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.fragment_holder, mFragment, Integer.toString(getFragmentCount()))
                        .addToBackStack(null)
                        .commit();
                return true;
            case R.id.action_back_to_shop:
                mFragment = ShopListFragment.newInstance(mCurrentPeem);
                getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.fragment_holder, mFragment, Integer.toString(getFragmentCount()))
                        .addToBackStack(null)
                        .commit();
                return true;
            default:
                // If we got here, the user's action was not recognized.
                // Invoke the superclass to handle it.
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onFragmentInteraction(Bundle arg) {
        String action;
        action = arg.getString(Constants.ACTION_KEY);

        if (action.equals(OrderSummaryFragment.ACTION_ORDER)) {
            Shop shop;
            Parcelable parcelable;
            parcelable = arg.getParcelable(Constants.DATA_KEY_1);
            shop = Parcels.unwrap(parcelable);
            fragmentManager(ShopFragment.newInstance(shop));
            return;
        }

        if (action.equals(OrderSummaryFragment.ACTION_HIDE_ACTION_BAR)) {
            showActionBar(false);
            return;
        }

        if (action.equals(OrderSummaryFragment.ACTION_SHOW_ACTION_BAR)) {
            showActionBar(true);
            return;
        }

    }

    private void fragmentManager(Fragment fragment) {

        if (fragment instanceof PeemFragment) {
            mFragment = fragment;
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment_holder, fragment, Integer.toString(getFragmentCount()))
                    .addToBackStack(PeemFragment.class.getSimpleName())
                    .commit();
            return;
        }

        if (fragment instanceof ShopFragment) {
            mFragment = fragment;
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment_holder, fragment, Integer.toString(getFragmentCount()))
                    .addToBackStack(ShopFragment.class.getSimpleName())
                    .commit();
            return;
        }

        if (fragment instanceof ShopListFragment) {
            mFragment = fragment;
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment_holder, fragment, Integer.toString(getFragmentCount()))
                    .addToBackStack(ShopListFragment.class.getSimpleName())
                    .commit();
            return;
        }

        if (fragment instanceof OrderSummaryFragment) {
            mFragment = fragment;
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment_holder, fragment, Integer.toString(getFragmentCount()))
                    .addToBackStack(OrderSummaryFragment.class.getSimpleName())
                    .commit();
            return;
        }
    }

    protected int getFragmentCount() {
        return getSupportFragmentManager().getBackStackEntryCount();
    }

    private Fragment getFragmentAt(int index) {
        return getFragmentCount() > 0 ? getSupportFragmentManager().findFragmentByTag(Integer.toString(index)) : null;
    }

    protected Fragment getCurrentFragment() {
        return getFragmentAt(getFragmentCount() - 1);
    }

    @Override
    public void onBackPressed() {
        // still not defined behavior
        if (mFragmentBackStackCounter > 0) {
            getSupportFragmentManager().popBackStack();
        } else if (mFragmentBackStackCounter == 0) {
            finish();
        }
    }

    @Override
    public void onBackStackChanged() {
        int entryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
        mFragmentBackStackCounter = entryCount;
        if (entryCount >= 0) {
            FragmentManager.BackStackEntry backStackEntry;
            backStackEntry = getSupportFragmentManager().getBackStackEntryAt(entryCount);
            /*if(mFragmentBackStackCounter > entryCount){
                mFragment = getSupportFragmentManager().findFragmentByTag(backStackEntry.getName());
            }*/
            mFragmentBackStackCounter = entryCount;
        }
    }

    private void showActionBar(boolean state) {
        ActionBar actionBar;
        actionBar = getSupportActionBar();
        /*if (actionBar != null) {
            actionBar.setTitle("SHOP");*/
        if (state) {
            if (actionBar != null) {
                actionBar.show();
            }
        } else {
            if (actionBar != null) {
                actionBar.hide();
            }
        }
    }

    private boolean isUserLoggedIn() {
        String accessToken;
        boolean isLoggedIn = true;
        accessToken = mPreferenceAdapter.readAccessToken();
        if (accessToken.isEmpty()) {
            isLoggedIn = false;
        }

        return isLoggedIn;
    }
}

This is the viewpager for the ShopFragment:

    public class ShopAdapter extends FragmentStatePagerAdapter {

    int mNumOfTabs;
    private final List<String> mTabTitles;
    Shop mShop;

    public enum TAB {
        WISHLIST(0), HISTORY(1), OFFERS(2), PRODUCTS(3);

        int value;

        TAB(int i) {
            this.value = i;
        }

        public int getValue() {
            return value;
        }
    }

    public ShopAdapter(FragmentManager fm, List<String> tabTitles, Shop shop) {
        super(fm);
        this.mTabTitles = tabTitles;
        this.mNumOfTabs = tabTitles.size();
        this.mShop = shop;
    }

    public void addTitle(String title) {
        mTabTitles.add(title);
    }

    @Override
    public Fragment getItem(int position) {

        switch (position) {
            case 0:
                return WishlistTabFragment.newInstance(mShop);
            case 1:
                return HistoryTabFragment.newInstance(mShop);
            case 2:
                return OffersTabFragment.newInstance(mShop);
            case 3:
                return ProductsFragment.newInstance(mShop);
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return mNumOfTabs;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        reloadData(position);
        return mTabTitles.get(position);
    }

    @Override
    public int getItemPosition(Object object) {
        return super.getItemPosition(object);
    }

    private void reloadData(int position) {
        RefreshResult result = new RefreshResult();
        result.setPageNumber(position);
        EventBus.getDefault().post(result);
    }
}

So the scenario leading to this error is the following: I call sendActionToActivity(ACTION_ORDER, mShop); and I am now in the ShopFragment. While being there I can see the toolbar with an item which when I click invokes this method in my activity:

case R.id.action_back_to_shop:
                mFragment = ShopListFragment.newInstance(mCurrentPeem);
                getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.fragment_holder, mFragment, Integer.toString(getFragmentCount()))
                        .addToBackStack(null)
                        .commit();
                return true;

And that is when the error happens. If I do this scenario but without calling sendActionToActivity(ACTION_ORDER, mShop); then everything works fine.

回答1:

In case anyone else is having this problem, I've changed my FragmentStatePagerAdapter to FragmentPagerAdapter and it did the trick. But in case you want to use FragmentStatePagerAdapter there are possible fixes on this link, where people had this issue.