使用 Dagger 2 和 setRetainInstance(true) 时获取 NullPointErexception;

Getting NullPointException when using Dagger2 and setRetainInstance(true);

所以我按照 Clean Architecture 来设计我的应用程序。我有一个 Activity 和一个视图寻呼机,其中有两个 Fragment。我通过 Dagger 为此注入 PagerAdapter

我知道在片段上调用 ​​setRetainInstance(true) 可以防止它被销毁,如果 Activity 被销毁,在这样的片段上调用 ​​getActivity() 可能会 return 出问题.在我的 activity 处于后台并且 activity 已(可能)被销毁后,我试图恢复我的 activity 时出现 NullPointException。

所以我的问题是

我的 activity 有一个 Dagger Component MainActivityComponent,它被注入如下。并且还扩展了 HasComponent。有关这方面的更多信息,请参阅 HasComponent

MainActivity.java

DaggerMainActivityComponent.builder()
 .applicationComponent(getApplicationComponent())
 .activityModule(getActivityModule())
 // Module for each fragment
 .conversationListModule(new ConversationListModule(this)) 
 .friendsListModule(new FriendsListModule(this))
 .build()
 .inject(this);

Getting the Activity's component

// Cause of the NullPointException getActivity()
protected <C> C getComponent(Class<C> componentType) {
    return componentType.cast(((HasComponent<C>) getActivity()).getComponent());
} 

如果你们有任何困惑,请告诉我。我知道我的解释很乱。谢谢

更新

似乎即使我删除 setRetainInstance(true) 也无法避免此错误。

问题

当应用程序在后台停留一段时间并且最近显示的activity被Android回收时出现问题。返回应用程序会导致 NullPointerException (NPE),因为 activity 的组件在初始化之前被片段调用(同时重新创建片段和 activity)。

解决方案

在BaseFragment中引入两个生命周期方法class。

  1. onInjectView() — 调用 onCreate(Bundle) 进行可选注入,如果抛出异常或返回 false,则再次调用 onActivityCreated(Bundle)。在此方法中,您可以获得注入组件并注入视图。如果注入成功,则返回true,则不会再次调用。
  2. 根据返回值,调用第二种方法。该方法名为 onViewInjected(Bundle),因为它仅在片段已注入且注入字段可以初始化时调用。

基础片段

public abstract class BaseFragment extends Fragment {

    private boolean mIsInjected = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);

        try {
            mIsInjected = onInjectView();
        } catch (IllegalStateException e) {
            Log.e(e.getClass().getSimpleName(), e.getMessage());
            mIsInjected = false;
        }
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (mIsInjected) onViewInjected(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (!mIsInjected) {
            mIsInjected = onInjectView();
            if (mIsInjected) onViewInjected(savedInstanceState);
        }
    }

    @SuppressWarnings("unchecked")
    protected <C> C getComponent(Class<C> componentType) throws IllegalStateException {
        C component = componentType.cast(((HasComponent<C>) getActivity()).getComponent());
        if (component == null) {
            throw new IllegalStateException(componentType.getSimpleName() + " has not been initialized yet.");
        }
        return component;
    }


    protected boolean onInjectView() throws IllegalStateException {
        // Return false by default.
        return false;
    }

    @CallSuper
    protected void onViewInjected(Bundle savedInstanceState) {
        // Intentionally left empty.
    }
 }

用法

public class SampleFragment extends BaseFragment implements SampleView {

    @Inject
    SamplePresenter mSamplePresenter;

    @Override
    protected boolean onInjectView() throws IllegalStateException {
        getComponent(SampleComponent.class).inject(this);
        return true;
    }

    @Override
    protected void onViewInjected(Bundle savedInstanceState) {
        super.onViewInjected(savedInstanceState);
        this.mSamplePresenter.setView(this);
    }
}

可以在 Efficient and bug-free fragment injection in Android MVP applications

上找到更多信息