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

Dagger 2: Injected object might still be null before onAttach is called in fragment

我正在使用 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,那是错误的。

而且这种情况从未发生过,除了可能发生 100000 次中的 1 次。只是几次崩溃。但想不通为什么。这种做法错了吗? Dagger 是否对参数化对象有某种问题?

我没有直接实例化 BaseFragment,所以该类型应该可以工作,而且它通常可以工作,那么为什么在某些情况下它不工作?

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

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

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

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

注入Fragment子类的正确方法在onAttach(Context context):

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

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

注意 super 调用之前的注入请求。这是根据 Dagger official documentation.

中的建议