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.
In case anyone else is having this problem, I've changed my
FragmentStatePagerAdapter
toFragmentPagerAdapter
and it did the trick. But in case you want to useFragmentStatePagerAdapter
there are possible fixes on this link, where people had this issue.