0

我的 Android 应用程序的载入是使用 FragmentStateAdapter 构建的,这是用户在安装应用程序后看到的第一个视图,即幻灯片列表。在某些用户上,应用程序在尝试显示第一张幻灯片时崩溃,因为幻灯片列表仍然是空的。我查看了代码,但找不到问题所在,所以我想与您分享:

这是我的 SlideViewerFragment 的摘录,我的应用程序中显示的第一个片段:

public class SlideViewerFragment extends BaseFragment {

    SlideAdapter mSlideAdapter;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    ...

        mSlideAdapter = new SlideAdapter(activity, myActivity);
        adapter.setValue(mSlideAdapter);
        slideList = viewModel.getSlideListData();
        slideList.observe(myActivity, this::slideListChanged);

        return view;
    }

    private void slideListChanged(SlideList slideListData) {

        slides = slideListData.slideList;
        currentSlideIndex = 0;
        for(int index = 0; index < slides.size(); index++)
        {
            SlideData slide = slides.get(index);
            mSlideAdapter.addSlide(slide);
        }

    ...
    }
}

这是我的幻灯片适配器:

public class SlideAdapter extends FragmentStateAdapter {

    private final Context mCtxt;
    private List<SlideData> listOfSlides;

    public SlideAdapter(FragmentActivity fa, Context ctxt) {
        super(fa);
        this.mCtxt = ctxt;
        this.listOfSlides = new ArrayList<>();
    }

    @Override
    public Fragment createFragment(int position) {
        return buildFragmentAtIndex(position);
    }

    @Override
    public int getItemCount() {
        return listOfSlides.size();
    }

    public void clearSlides()
    {
        listOfSlides = new ArrayList<>();
        FirebaseCrashlytics.getInstance().log("E/LISTVIEW: SlideAdapter.clearSlides");
        FirebaseCrashlytics.getInstance().log("E/SLIDER: SlideAdapter.clearSlides");
        notifyDataSetChanged();
    }

    public void addSlide(SlideData data)
    {
        listOfSlides.add(data);
        FirebaseCrashlytics.getInstance().log("E/LISTVIEW: SlideAdapter.addSlide");
        FirebaseCrashlytics.getInstance().log("E/SLIDER: SlideAdapter.addSlide");
        notifyDataSetChanged();
    }

    private Fragment buildFragmentAtIndex(int index) {

        SlideViewModelInterface viewModel = new ViewModelProvider((AppCompatActivity)mCtxt).get(SlideViewModel.class);
        if(index == 0) {
            FirebaseCrashlytics.getInstance().log("E/SLIDER: In Adapter, going to clear slides");
            viewModel.clearSlides();
        }
        SlideData data = listOfSlides.get(index);
        FirebaseCrashlytics.getInstance().log("E/SLIDER: In Adapter, going to add slide "+index);
        viewModel.setSlideData(data);
        FragmentFactory ff = new SlideFragmentFactory(index);
        return ff.instantiate(mCtxt.getClassLoader(), SlideFragment.class.getName());
    }

}

这是我的 SlideFragmentFactory:

public class SlideFragmentFactory extends FragmentFactory {

    private final int index;

    public SlideFragmentFactory(int index) {
        this.index = index;
    }

    @NonNull
    @Override
    public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {

        Class clazz = loadFragmentClass(classLoader, className);
        Fragment fragment;
        if (clazz == SlideFragment.class) {
            fragment = SlideFragment.newInstance(index);
        } else {
            return super.instantiate(classLoader, className);
        }
        return fragment;    
    }

}

这是我的幻灯片片段:

public class SlideFragment extends Fragment {

    public static SlideFragment newInstance(int index) {

        SlideFragment slide = new SlideFragment();
        Bundle args = new Bundle();
        args.putInt("index", index);
        slide.setArguments(args);
        return slide;
    }

    public SlideFragment() {
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FirebaseCrashlytics.getInstance().log("E/SLIDER: SliderFragment onCreate");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        FirebaseCrashlytics.getInstance().log("E/SLIDER: SliderFragment onCreateView");
        View _fragment = inflater.inflate(R.layout.fragment_slide, container, false);
        LinearLayout wrapper = _fragment.findViewById(R.id.wrapper);

        Bundle args = getArguments();
        int index = args.getInt("index");
        SlideViewModelInterface viewModel = new ViewModelProvider(requireActivity()).get(SlideViewModel.class);
        FirebaseCrashlytics.getInstance().log("E/SLIDER: SliderFragment, is going to get slide "+index);
        SlideData data = viewModel.getSlideData(index);
        Slide.buildSlide(getActivity(), data, wrapper);

        return _fragment;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

}

最后,这是我的 SlideViewModel:

public class SlideViewModel extends AndroidViewModel implements SlideViewModelInterface {

    private List<SlideData> slides;

    public SlideViewModel(Application application) {
        super(application);

    }

    public void clearSlides() {
        slides = new ArrayList<>();
        FirebaseCrashlytics.getInstance().log("E/SLIDER: Clear slides");
    }

    public void setSlideData(SlideData data) {
        slides.add(data);
        FirebaseCrashlytics.getInstance().log("E/SLIDER: Add slide");
    }

    public SlideData getSlideData(int index) {
        FirebaseCrashlytics.getInstance().log("E/SLIDER: Get slide "+index);
        if(slides == null) {
            FirebaseCrashlytics.getInstance().log("E/SLIDER: But slides is null!");
        } else {
            FirebaseCrashlytics.getInstance().log("E/SLIDER: Slides has "+slides.size()+" slides");
        }
        return slides.get(index);
    }

}

如您所见,我添加了一些 Crashlytics 日志。好吧,应用程序在 SlideViewModel:getSlideData 中崩溃,行“return slides.get(index)”,因为 slides 为空。

Crashlytics 日志显示:

03:24:13.082 AM E/SLIDER: SliderFragment onCreate
03:24:13.204 AM E/SLIDER: SliderFragment onCreateView
03:24:13.206 AM E/SLIDER: SliderFragment, is going to get slide 0 

在此之后,崩溃:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
       at com.rfdevelopments.genomapp.entities.SlideViewModel.getSlideData(SlideViewModel.java:57)
       at com.rfdevelopments.genomapp.ui.fragments.SlideFragment.onCreateView(SlideFragment.java:56)
       at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
       at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
       at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
       at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
       at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
       at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3126)
       at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3070)
       at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:251)
       at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:501)
       at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
       at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
       at android.app.Activity.performStart(Activity.java:8222)
       at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3818)
       at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
       at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
       at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:246)
       at android.app.ActivityThread.main(ActivityThread.java:8512)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)

我不知道为什么会出现这些日志,但 SlideAdapter:buildFragmentAtIndex 中的日志没有出现......

知道我在这里想念什么吗?

编辑:

在重新阅读了我的问题、日志并受到 Ramesh 评论的启发后,我认为问题在于(我不知道为什么)我的 SlideFragment 是在 FragmentStateAdapter 之前构建的,所以我重构了部分代码以更改施工流程。

现在,我的 SlideAdapter 在构造函数中获取幻灯片列表:

public class SlideAdapter extends FragmentStateAdapter {

    private final Context mCtxt;
    private List<SlideData> listOfSlides;

    public SlideAdapter(FragmentActivity fa, Context ctxt, List<SlideData> slideListData) {
        super(fa);
        this.mCtxt = ctxt;
        this.listOfSlides = slideListData;
        notifyDataSetChanged();
    }

    @Override
    public Fragment createFragment(int position) {
        return buildFragmentAtIndex(position);
    }

    @Override
    public int getItemCount() {
        return listOfSlides.size();
    }

    private Fragment buildFragmentAtIndex(int index) {

        SlideViewModelInterface viewModel = new ViewModelProvider((AppCompatActivity)mCtxt).get(SlideViewModel.class);
        if(index == 0) {
            FirebaseCrashlytics.getInstance().log("E/SLIDER: In Adapter, going to clear slides");
            viewModel.clearSlides();
        }
        SlideData data = listOfSlides.get(index);
        FirebaseCrashlytics.getInstance().log("E/SLIDER: In Adapter, going to add slide "+index);
        viewModel.setSlideData(data);
        FragmentFactory ff = new SlideFragmentFactory(index);
        return ff.instantiate(mCtxt.getClassLoader(), SlideFragment.class.getName());
    }

}

SlideViewerFragment 也发生了变化,只有在我准备好幻灯片列表时才构建适配器:

public class SlideViewerFragment extends BaseFragment {

    SlideAdapter mSlideAdapter;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        ...

        slideList = viewModel.getSlideListData();
        slideList.observe(myActivity, this::slideListChanged);

        return view;
    }

    private void slideListChanged(SlideList slideListData) {

        slides = slideListData.slideList;
        mSlideAdapter = new SlideAdapter(activity, myActivity, slides);
        adapter.setValue(mSlideAdapter);
        currentSlideIndex = 0;

        ...
    }
}

我很确定这会修复错误,但不幸的是,我仍然遇到同样的崩溃,日志完全相同。这怎么可能在 SlideAdapter 的日志之前调用 SlideFramgent 的日志?

4

0 回答 0