Dagger 2:在片段中调用onAttach之前,注入的对象可能仍为null

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dagger 2:在片段中调用onAttach之前,注入的对象可能仍为null相关的知识,希望对你有一定的参考价值。

我正在使用Dagger将viewModel注入片段:

class BaseFragment<T extends BaseViewModel> extends Fragment {

    @Inject T viewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if(viewModel == null) {
            throw new RuntimeException("Viewmodel was null: "+getClass());
        }
        viewModel.setContext(context);
        viewModel.onAttach(context);
    }

}

class MyFragment extends BaseFragment<MyViewModel> {

    public MyFragment() {
        MyApp.getInstance().getComponent().inject(this);
        //viewModel should be available at this point, before OnAttach is called
    }

}

所以简而言之,我在构造函数中注入了viewModel,如果onAttach仍然为null,则说明错了。

而这种情况从未发生过,除非它有100万次中的1次。只是几次崩溃。但无法弄清楚为什么。这种方法有误吗? Dagger是否有某种方式存在参数化对象的问题?

我不直接实例化BaseFragment所以类型应该工作,它通常这样做,那么为什么它在某些情况下不起作用?

答案

注入Fragment的构造函数是不正确的:

public MyFragment() {
    //MyApp.getInstance().getComponent().inject(this);    
    //don't inject in a constructor!  
}

虽然这可能适用于非常简单的工作流程,但它无法正确处理Fragment生命周期。特别是,存在片段存在但与活动分离的情况。当发生这种情况并且有必要将片段显示给再次用户时,android OS将尝试重新附加缓存的片段而不调用构造函数(因为实例已经存在)。因为你依赖的假设是在onAttach之前总是新建一个构造函数,所以可以想象这种情况会导致你的崩溃。

虽然通过与您的应用程序的正常交互可能很难自己复制此问题,但我怀疑如果您在启用System/DeveloperOptions/Don't keep activities的情况下测试应用程序,则更有可能遇到此问题。

注入片段子类的正确方法是在onAttach(Context context)中:

@Override
public void onAttach(Context context) {
    MyApp.getInstance().getComponent().inject(this);
    super(context); //call super
}

这将更准确地跟踪Fragment生命周期。

请注意super通话前的注射请求。这是根据Dagger official documentation的建议。

以上是关于Dagger 2:在片段中调用onAttach之前,注入的对象可能仍为null的主要内容,如果未能解决你的问题,请参考以下文章

替换未调用 onAttach、onCreate、onCreateView 等的活动中的片段

在 onActivityCreated() 之后再次调用 Fragment 的 onAttach()

OnDetach/onAttach 片段重新创建片段活动

片段的 onAttach 中的值的可能性是啥

片段交互回调:onAttach() vs setter

是否最好使用 Activity.onAttachFragment 或 Fragment.onAttach 在 Activity 和嵌套片段之间进行通信?