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

标签 android android-fragments android-lifecycle dagger-2 clean-architecture

所以我关注了Clean Architecture设计我的应用程序。我有一个带有 View 寻呼机的 Activity,其中有两个 Fragment。我为此通过 Dagger 注入(inject)了 PagerAdapter

我知道在 fragment 上调用 ​​setRetainInstance(true) 可以防止它被销毁,如果 Activity 是被毁。在 Activity 处于后台并且 Activity 已(可能)被销毁后尝试恢复我的 Activity 时,我收到 NullPointException。

所以我的问题是

  • 有没有更好的方法来完成我想做的事情?
  • 有什么资源可以指点我吗?
  • 还不确定如果 fragment 已被销毁,为什么 fragment 和适配器仍处于 Activity 状态。使用 LeakCanary 时我没有发现内存泄漏。

我的 Activity 有一个 Dagger Component MainActivityComponent 注入(inject)如下。并且还扩展了 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 的组件在初始化之前被 fragment 调用(同时重新创建 fragment 和 Activity )。

解决方案

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

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

基础 fragment

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 上找到

关于android - 使用 Dagger 2 和 setRetainInstance(true) 时获取 NullPointErexception;,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47406449/

相关文章:

android - onPause、onResume、onStop、onStart 等不触发 - 生命周期?

android - setColorFilter() 和可变绘图

java - Android Studio : How to get EditText values from Fragment 1 to Fragment 2 respectively on button clicked

安卓 fragment : crash on screen orientation change

java - 如何在抽屉导航中的 fragment 上显示 SQLite 数据库数据?

android - 在最小化我的应用程序并再次打开它时,为什么我的应用程序再次从启动屏幕启动?

android - 在数据绑定(bind)上调用 setLifecycleOwner 时,我应该传递从 getViewLifecycleOwner 返回的 Fragment 实例还是所有者

java - 读取远程txt文件android

java - 唤醒锁和 wifilock 不工作

android - 如何使用 ionic-cli 使用 Crosswalk lite 构建 android?