使用 getChildFragmentManager().beginTransaction() 时的空对象引用

Posted

技术标签:

【中文标题】使用 getChildFragmentManager().beginTransaction() 时的空对象引用【英文标题】:Null object reference when using getChildFragmentManager().beginTransaction() 【发布时间】:2017-12-02 15:01:25 【问题描述】:

我的 ViewPgaer 中有 4 个使用 FragmentPagerAdapter 的片段。我尝试在不断延迟后添加每个片段,以便在切换底部栏选项卡时获得无抖动动画。

我使用this 库作为具有移动动画的底栏。所有四个片段都有多个 api 调用,如果我在延迟后不添加这些,则移动动画很生涩。

问题是我收到了一个难以复制的错误日志(发布在下面)。

错误日志:

    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);

报错日志就行了

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

【问题讨论】:

在 beginTransation() 之前检查片段中的 getactivity() !=null。 你是救世主!但问题不是因为活动为空,我在事务中添加了一个空片段!不管怎样,你节省了我的时间 【参考方案1】:

创建一个扩展片段的抽象片段。像这样

    public abstract BaseChildFragment extends Fragment 
  public void onViewPagerPagechange(); 

使所有 ChildFragment 扩展 BaseChildFragment(FragmentUserProfile, FragmentUserLike,FragmentUserWellet,ETC) , 在您的适配器上添加以下代码。

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;

在您的 Fragment 上具有像这样的 View pager AddpageChangeListener

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)  

);

这将调用您的 ChildFragment onViewPagerPagechange(); 方法。您可以在其中加载您的子片段。

【讨论】:

【参考方案2】:

你不能这样做

public static Fragment create(Fragment frag, int timeMs) 
    FragmentFake fragmentFake = new FragmentFake();
    Bundle bundle = new Bundle();
    //remove this line, it won't work
    fragmentFake.fragment = frag;
    bundle.putInt("time", timeMs);
    fragmentFake.setArguments(bundle);
    return fragmentFake;

您可以从 create 语句中删除该分配行,并在创建实例后添加另一个方法来分配片段。你的代码应该是这样的

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


public void setFragment(Fragment fragment)
    this.fragment = fragment;


//How you assign fragment 
FragmentFake fakeFragment = FragmentFake.create(0);
fakeFragment.setFragment(yourFragment);

然后进行分片交易

【讨论】:

但是实例是在分配片段之前创建的。你能告诉我它是如何解决这个问题的吗?因为这个问题不经常发生,我不知道如何复制它。

以上是关于使用 getChildFragmentManager().beginTransaction() 时的空对象引用的主要内容,如果未能解决你的问题,请参考以下文章

使用嵌套片段和动画对象

Android 动画嵌套片段

Android Fragment使用 嵌套Fragments (Nested Fragments) 的使用及常见错误

Android Fragment使用 嵌套Fragments (Nested Fragments) 的使用及常见错误

onActivityResult() 未在新的嵌套片段 API 中调用

fragment中的fragment如何显示出来