我的 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 的日志?