使用 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。
所以我的问题是
- 有没有更好的方法来完成我想做的事情?
- 有人可以指点我的任何资源吗?
- 也不确定为什么片段被销毁后片段和适配器仍然处于活动状态。 LeakCanary 没有内存泄漏。
我的 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。
- onInjectView() — 调用 onCreate(Bundle) 进行可选注入,如果抛出异常或返回 false,则再次调用 onActivityCreated(Bundle)。在此方法中,您可以获得注入组件并注入视图。如果注入成功,则返回true,则不会再次调用。
- 根据返回值,调用第二种方法。该方法名为 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
上找到更多信息
所以我按照 Clean Architecture 来设计我的应用程序。我有一个 Activity
和一个视图寻呼机,其中有两个 Fragment
。我通过 Dagger 为此注入 PagerAdapter
。
我知道在片段上调用 setRetainInstance(true)
可以防止它被销毁,如果 Activity 被销毁,在这样的片段上调用 getActivity()
可能会 return 出问题.在我的 activity 处于后台并且 activity 已(可能)被销毁后,我试图恢复我的 activity 时出现 NullPointException。
所以我的问题是
- 有没有更好的方法来完成我想做的事情?
- 有人可以指点我的任何资源吗?
- 也不确定为什么片段被销毁后片段和适配器仍然处于活动状态。 LeakCanary 没有内存泄漏。
我的 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。
- onInjectView() — 调用 onCreate(Bundle) 进行可选注入,如果抛出异常或返回 false,则再次调用 onActivityCreated(Bundle)。在此方法中,您可以获得注入组件并注入视图。如果注入成功,则返回true,则不会再次调用。
- 根据返回值,调用第二种方法。该方法名为 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
上找到更多信息