Search code examples
androidandroid-fragmentsfragmentfragmentmanager

Null object reference when using getChildFragmentManager().beginTransaction()


I have 4 fragments in my ViewPgaer using FragmentPagerAdapter. I try to add each fragment after a constant delay to have a jerk free animation while switching bottom bar tabs.

I use this library for the bottom bar which has a shifting animation. All four fragments have multiple api calls and if I don't add these after a delay, the shifting animation is jerky.

The problem is I get an error log (posted below) which is hard to replicate.

Error Log:

    Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
   at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:380)
   at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:369)
   at com.vanitee.services.view.FragmentFake$1.run(FragmentFake.java:46)
   at android.os.Handler.handleCallback(Handler.java:761)
   at android.os.Handler.dispatchMessage(Handler.java:98)
   at android.os.Looper.loop(Looper.java:156)
   at android.app.ActivityThread.main(ActivityThread.java:6585)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831)

ViewPagerAdapterClass:

    class MyAccountPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        if (position == ME_TAB) {
            if (Utility.isEmpty(fragmentProfile)) {
                fragmentProfile = FragmentFake.create(new FragmentUserProfile(), 50);
            }
            return fragmentProfile;
        } else if (position == BOOKINGS_TAB) {
            if (Utility.isEmpty(fragmentBooking)) {
                fragmentBooking = FragmentFake.create(new FragmentBookings(), 100);
            }
            return fragmentBooking;
        } else if (position == WALLET_TAB) {
            if (Utility.isEmpty(fragmentWallet)) {
                fragmentWallet = FragmentFake.create(new FragmentWallet(), 150);
            }
            return fragmentWallet;
        } else if (position == LIKES_TAB) {
            if (Utility.isEmpty(fragmentLikes)) {
                fragmentLikes = FragmentFake.create(new FragmentLikes(), 200);
            }
            return fragmentLikes;
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case ME_TAB:
                return getContext().getString(R.string.me_tab);
            case BOOKINGS_TAB:
                return getContext().getString(R.string.bookings_tab);
            case WALLET_TAB:
                return getContext().getString(R.string.wallet_tab);
            case LIKES_TAB:
                return getContext().getString(R.string.likes_tab);
            default:
                return null;
        }
    }

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

FragmentFake:

 public class FragmentFake extends Fragment {

private View root;
Fragment fragment;

public static Fragment create(Fragment frag, int timeMs) {
    FragmentFake fragmentFake = new FragmentFake();
    Bundle bundle = new Bundle();
    fragmentFake.fragment = frag;
    bundle.putInt("time", timeMs);
    fragmentFake.setArguments(bundle);
    return fragmentFake;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    root = inflater.inflate(R.layout.layout_fake_fragment, container, false);
    Bundle bundle = getArguments();
    replaceFragment(bundle.getInt("time"));
    return root;
}

private void replaceFragment(int time) {
    root.postDelayed(new Runnable() {
        @Override
        public void run() {
            if(!isAdded()){
                return;
            }
            getChildFragmentManager().beginTransaction().add(R.id.replace_fragment_container, fragment).commit();
        }
    }, time);
}

}

The Error log reported is on the line

     getChildFragmentManager().beginTransaction().add(R.id.replace_fragment_container, fragment).commit();

Solution

  • Create one Abstract Fragment that extends Fragment. like this

        public abstract BaseChildFragment extends Fragment{ 
      public void onViewPagerPagechange(); 
    

    }

    Make All ChildFragment extends BaseChildFragment(FragmentUserProfile, FragmentUserLike,FragmentUserWellet,ETC) ,
    On your Adapter add below code.

    LruCache<Integer,BaseChildFragment> cache = new LruCache<>(4);
    public BaseChildFragment getBaseChildFragment(int position){
       return cache.get(position);
    }
    
     @Override
    public Fragment getItem(int position) {
        if (position == ME_TAB) {
            if (Utility.isEmpty(fragmentProfile)) {
                fragmentProfile = FragmentFake.create(new FragmentUserProfile(), 50);
                cache.put(position,fragmentProfile );
            }
            return fragmentProfile;
        } else if (position == BOOKINGS_TAB) {
            if (Utility.isEmpty(fragmentBooking)) {
                fragmentBooking = FragmentFake.create(new FragmentBookings(), 100);
    cache.put(position,fragmentBooking );
            }
            return fragmentBooking;
        } else if (position == WALLET_TAB) {
            if (Utility.isEmpty(fragmentWallet)) {
                fragmentWallet = FragmentFake.create(new FragmentWallet(), 150); 
                cache.put(position,fragmentWallet );
            }
            return fragmentWallet;
        } else if (position == LIKES_TAB) {
            if (Utility.isEmpty(fragmentLikes)) {
                fragmentLikes = FragmentFake.create(new FragmentLikes(), 200);
              cache.put(position,fragmentLikes );
            }
            return fragmentLikes;
        }
        return null;
    }
    

    On your Fragment that have View pager AddpageChangeListener like this

    viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
    
    // optional 
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
    
    // optional 
    @Override
    public void onPageSelected(int position) {
     BaseChildFragment fragment =   adapter.getBaseChildFragment(position);
    fragmeng.onViewPagerPagechange(); 
     }
        // optional 
        @Override
        public void onPageScrollStateChanged(int state) { }
    
    });
    

    This will call your ChildFragment onViewPagerPagechange(); method. where you can load your child fragments.