Dagger 2.11 ContributesAndroidInjector 单例依赖注入失败

Dagger 2.11 ContributesAndroidInjector Singleton dependency injection fails

我正在探索 Dagger 2.11 中的新 dagger.android。我希望不必创建像 @PerActivity 这样的自定义范围注释。到目前为止,我能够执行以下操作:

1) 定义应用程序范围单例并将它们注入到活动中。

2) 定义 Activity 范围非单例依赖项并使用 @ContributesAndroidInjector

将它们注入到它们的活动中

我想不通的是如何使用应用程序范围单例和 Activity 范围非单例。

在下面的示例中,我希望我的 Activity 作用域 MyActivityDependencyAMyActivityDependencyB 能够访问单例 MyActivityService

下面的设置导致:

Error:(24, 3) error: com.example.di.BuildersModule_BindMyActivity.MyActivitySubcomponent (unscoped) may not reference scoped bindings: @Singleton @Provides com.example.MyActivityService com.example.MyActivitySingletonsModule.provideMyActivityService()

这是我的设置。注意,我定义了单独的 MyActivitySingletonsModuleMyActivityModule 因为我不能在同一个模块文件中混合单例和非单例依赖。

@Module
public abstract class BuildersModule {
    @ContributesAndroidInjector(modules = {MyActivitySingletonsModule.class, MyActivityModule.class})
    abstract MyActivity bindMyActivity();
    }
}

@Module
public abstract class MyActivityModule {
    @Provides
    MyActivityDependencyA provideMyActivityDependencyA(MyActivityService myActivityService){
       return new MyActivityDependencyA(myActivityService);
    }
    @Provides
    MyActivityDependencyB provideMyActivityDependencyB(MyActivityService myActivityService) {
        return new MyActivityDependencyB(myActivityService);
    }
}

@Module
public abstract class MyActivitySingletonsModule {
    @Singleton
    @Provides
    MyActivityService provideMyActivityService() {
        return new MyActivityService();
    }
}

@Singleton
 @Component(modules = {
    AndroidSupportInjectionModule.class,
    AppModule.class,
    BuildersModule.class})

public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(App application);
        AppComponent build();
    }
    void inject(App app);
}

是否可以在不定义自定义范围注释的情况下完成我想做的事情?

提前致谢!

这里有一些问题:首先,ActivitySingleton没有多大意义。依赖项要么是单例(每个应用程序或应用程序范围),要么不是。

如果它不是单例,它可能是 activity 范围的(根据 activity)。这意味着它与 Activity 同生同死,也就是说,它的生命周期与 Activity 本身的生命周期一致,因此它将与 [=37= 的 onDestroy 一起被摧毁].

这并不意味着在 Activity 中注入的所有内容都必须是 @PerActivity。您仍然可以在那里注入 @Singleton 依赖项(例如每个应用程序 OkHttpClient)。但是,这些 @Singleton 依赖项不会绑定到构成 @PerActivity 组件的模块集中。而是绑定在父组件的模块集中,通过组件层级(依赖组件或子组件)获取。

这些说明你的ActivitySingletonsModule不正确,看下面代码中的注释:

@Module
public abstract class MyActivitySingletonsModule {
    //@Singleton
    //^^ remove the annotation here if you want to use the
    //in your ActivityComponent

    //If you need this as a per-app singleton, then include 
    //this module at the AppComponent level
    @Provides
    MyActivityService provideMyActivityService() {
        return new MyActivityService();
    }
}

我不明白为什么不愿意定义自定义范围。它们非常轻巧,可以提高可读性。这是创建 @PerActivity 范围所需的单行代码。

@Scope @Retention(RetentionPolicy.RUNTIME) public @interface PerActivity {}

我怀疑范围的概念不清楚,这导致了不愿意。不可否认,这可能相当令人困惑。但是,有一些非常好的规范答案可以帮助澄清。我会建议这个问题作为开始:

为什么要避免自定义范围? Dagger 2.10+ 中引入的新 dagger.android 依赖注入框架仍然需要自定义作用域。

"My understanding is @ContributesAndroidInjector removes the need for custom annotation and I was able to prove it by using non-singletons defined in the activity scope without any issues."

@ContributesAndroidInjector(在 v2.11 中可用)不会消除对自定义范围的需求。它只是取代了声明 @Subcomponent 类 的需要,而不是使用 @Subcomponent.Builder 在运行时注入组件所需的依赖项。请查看 official dagger.android user guide about @ContributesAndroidInjector;

中的以下代码片段

"Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use @ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with @ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well."

@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();

这里的关键是"If the subcomponent needs scopes, apply the scope annotations to the method as well."

查看以下代码,了解如何将 @Singleton@PerActivity@PerFragment@PerChildFragment 自定义范围与新的 dagger.android 注入框架.

// Could also extend DaggerApplication instead of implementing HasActivityInjector
// App.java
public class App extends Application implements HasActivityInjector {

    @Inject
    AppDependency appDependency;

    @Inject
    DispatchingAndroidInjector<Activity> activityInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.create().inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return activityInjector;
    }
}

// AppModule.java
@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
    @PerActivity
    @ContributesAndroidInjector(modules = MainActivityModule.class)
    abstract MainActivity mainActivityInjector();
}

// AppComponent.java
@Singleton
@Component(modules = AppModule.class)
interface AppComponent {
    void inject(App app);
}

// Could also extend DaggerActivity instead of implementing HasFragmentInjector
// MainActivity.java
public final class MainActivity extends Activity implements HasFragmentInjector {

    @Inject
    AppDependency appDependency; // same object from App

    @Inject
    ActivityDependency activityDependency;

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentInjector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        if (savedInstanceState == null) {
            addFragment(R.id.fragment_container, new MainFragment());
        }
    }

    @Override
    public final AndroidInjector<Fragment> fragmentInjector() {
        return fragmentInjector;
    }
}

// MainActivityModule.java
@Module
public abstract class MainActivityModule {
    @PerFragment
    @ContributesAndroidInjector(modules = MainFragmentModule.class)
    abstract MainFragment mainFragmentInjector();
}

// Could also extend DaggerFragment instead of implementing HasFragmentInjector
// MainFragment.java
public final class MainFragment extends Fragment implements HasFragmentInjector {

    @Inject
    AppDependency appDependency; // same object from App

    @Inject
    ActivityDependency activityDependency; // same object from MainActivity

    @Inject
    FragmentDependency fragmentDepency; 

    @Inject
    DispatchingAndroidInjector<Fragment> childFragmentInjector;

    @SuppressWarnings("deprecation")
    @Override
    public void onAttach(Activity activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            // Perform injection here before M, L (API 22) and below because onAttach(Context)
            // is not yet available at L.
            AndroidInjection.inject(this);
        }
        super.onAttach(activity);
    }

    @Override
    public void onAttach(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Perform injection here for M (API 23) due to deprecation of onAttach(Activity).
            AndroidInjection.inject(this);
        }
        super.onAttach(context);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.main_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null) {
            addChildFragment(R.id.child_fragment_container, new MainChildFragment());
        }
    }

    @Override
    public final AndroidInjector<Fragment> fragmentInjector() {
        return childFragmentInjector;
    }
}

// MainFragmentModule.java
@Module
public abstract class MainFragmentModule {
    @PerChildFragment
    @ContributesAndroidInjector(modules = MainChildFragmentModule.class)
    abstract MainChildFragment mainChildFragmentInjector();
}

// MainChildFragment.java
public final class MainChildFragment extends Fragment {

    @Inject
    AppDependency appDependency; // same object from App

    @Inject
    ActivityDependency activityDependency; // same object from MainActivity

    @Inject
    FragmentDependency fragmentDepency; // same object from MainFragment

    @Inject
    ChildFragmentDependency childFragmentDepency;

    @Override
    public void onAttach(Context context) {
        AndroidInjection.inject(this);
        super.onAttach(context);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.main_child_fragment, container, false);
    }
}

// MainChildFragmentModule.java
@Module
public abstract class MainChildFragmentModule {
}

// PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}

// PerFragment.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerFragment {
}

// PerChildFragment.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerChildFragment {
}

// AppDependency.java
@Singleton
public final class AppDependency {
    @Inject
    AppDependency() {
    }
}

// ActivityDependency.java
@PerActivity
public final class ActivityDependency {
    @Inject
    ActivityDependency() {
    }
}

// FragmentDependency.java
@PerFragment
public final class FragmentDependency {
    @Inject
    FragmentDependency() {
    }
} 

// ChildFragmentDependency.java
@PerChildFragment
public final class ChildFragmentDependency {
    @Inject
    ChildFragmentDependency() {
    }
}

有关使用 @ContributesAndroidInjector 和上述自定义范围的完整 dagger.android 2.11 设置指南,请阅读 this article