您如何组织Dagger 2模块和组件?

iva*_*acf 36 java android dagger-2

你有一个特定的包,你把所有与Dagger相关的课程放在哪里?

或者你把它们放在他们注入的相关课程旁边,例如,如果你有一个MainActivityModuleMainActivityComponent,你把它们放在与你相同的包装中MainActivity.

此外,我见过很多人将组件定义为内部类,例如ApplicationComponent,在Application类中定义的组件.你认为这是一个好习惯吗?

Epi*_*rce 26

编辑:让我先从这里接近真相的事实开始,但这是一个反模式,正如Martin Fowler的Data Domain Presentation Layering文章HERE(点击链接!)所述,它指明你不应该有a MapperModule和a PresenterModule,你应该有一个GalleryModule和一个SomeFeatureModule有所有的映射器,演示者等.

实现它的智能路线是使用组件依赖关系来为您拥有的每个功能提供原始单例组件.我所描述的是"全栈"分层, 功能分离.

下面写的是"反模式",您可以将应用程序的顶级模块切割为"层".这样做有很多缺点.不要这样做.但是你可以阅读它并学习不该做的事情.

原文:

通常,只要整个应用程序存在,您就会使用Component类似的单个ApplicationComponent来包含您在整个应用程序中使用的所有单例依赖项.您可以在Application类中实例化它,并从其他地方访问它.

我目前的项目结构是:

+ injection
|- components
   |-- ApplicationComponent.java
|- modules
   |- data
      |-- DbMapperModule.java
      |-- ...
   |- domain
      |-- InteractorModule.java
      |-- ...
   |- presentation
      |-- ...
   |- utils
      |-- ...
|- scope
|- subcomponents
   |- data
      |-- ...
   |- domain
      |-- DbMapperComponent.java
      |-- ...
   |- presentation
      |-- ...
   |- utils
      |-- ...
   |-- AppContextComponent.java
   |-- AppDataComponent.java
   |-- AppDomainComponent.java
   |-- AppPresentationComponent.java
   |-- AppUtilsComponent.java
Run Code Online (Sandbox Code Playgroud)

例如,我的是这样的:

public enum Injector {
    INSTANCE;
    private ApplicationComponent applicationComponent;

    private Injector() {
    }

    public ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }

    ApplicationComponent initializeApplicationComponent(CustomApplication customApplication) {
        AppContextModule appContextModule = new AppContextModule(customApplication);
        RealmModule realmModule = new RealmModule(customApplication.getRealmHolder());
        applicationComponent = DaggerApplicationComponent.builder()
                .appContextModule(appContextModule)
                .realmModule(realmModule)
                .build();
        return applicationComponent;
    }
}
Run Code Online (Sandbox Code Playgroud)

而且你需要一个ApplicationComponent可以注入任何你希望场注入的类的受包保护的字段.

@Singleton
@Component(modules = {
        AppContextModule.class,
        DbMapperModule.class,
        DbTaskModule.class,
        RealmModule.class,
        RepositoryModule.class,
        InteractorModule.class,
        ManagerModule.class,
        ServiceModule.class,
        PresenterModule.class,
        JobManagerModule.class,
        XmlPersisterModule.class
})
public interface ApplicationComponent
        extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
    void inject(CustomApplication customApplication);

    void inject(DashboardActivity dashboardActivity);

    ...
}
Run Code Online (Sandbox Code Playgroud)

对我来说,AppContextComponent将是一个@Subcomponent,但这实际上并不意味着它.这些只是创建子范围的一种方法,而不是将组件切割成较小部分的方法.所以我继承的接口实际上只是一个普通interface的提供方法.其他人也一样.

public interface AppContextComponent {
    CustomApplication customApplication();

    Context applicationContext();

    AppConfig appConfig();

    PackageManager packageManager();

    AlarmManager alarmManager();
}
Run Code Online (Sandbox Code Playgroud)

组件依赖性(允许您像子组件一样进行子视图)不允许多个作用域组件,这也意味着您的模块将是未作用域的.这是因为您无法从多个范围继承,就像您无法从Java中的多个类继承一样.

无范围提供程序使得模块不会在每次注入调用时保留单个实例,而是保留新实例.要获得作用域依赖项,还需要在模块提供程序方法上提供作用域.

@Module
public class InteractorModule {
    @Provides
    @Singleton
    public LeftNavigationDrawerInteractor leftNavigationDrawerInteractor() {
        return new LeftNavigationDrawerInteractorImpl();
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

在应用程序中,如果在任何地方使用Singleton组件,除非创建子范围,否则不需要更多组件.如果需要,您甚至可以考虑使您的模块成为您的视图和演示者的完整数据提供者.

@Component(dependencies = {ApplicationComponent.class}, modules = {DetailActivityModule.class}) 
@ActivityScope
public interface DetailActivityComponent extends ApplicationComponent {
    DataObject data();

    void inject(DetailActivity detailActivity);
}

@Module
public class DetailActivityModule {
    private String parameter;

    public DetailActivityModule(String parameter) {
        this.parameter = parameter;
    }

    @Provides
    public DataObject data(RealmHolder realmHolder) {
        Realm realm = realmHolder.getRealm();
        return realm.where(DataObject.class).equalTo("parameter", parameter).findFirst();
    }
}
Run Code Online (Sandbox Code Playgroud)

Subscoping允许您拥有演示者的多个实例,然后可以存储状态.这在例如Mortar/Flow中是有意义的,其中每个屏幕具有其自己的"路径",并且每个路径具有其自己的组件 - 以将数据提供为"蓝图".

public class FirstPath
        extends BasePath {
    public static final String TAG = " FirstPath";

    public final int parameter;

    public FirstPath(int parameter) {
        this.parameter = parameter;
    }

    //...

    @Override
    public int getLayout() {
        return R.layout.path_first;
    }

    @Override
    public FirstViewComponent createComponent() {
        FirstPath.FirstViewComponent firstViewComponent = DaggerFirstPath_FirstViewComponent.builder()
                .applicationComponent(InjectorService.obtain())
                .firstViewModule(new FirstPath.FirstViewModule(parameter))
                .build();
        return firstViewComponent;
    }

    @Override
    public String getScopeName() {
        return TAG + "_" + parameter;
    }

    @ViewScope //needed
    @Component(dependencies = {ApplicationComponent.class}, modules = {FirstViewModule.class})
    public interface FirstViewComponent
            extends ApplicationComponent {
        String data();

        FirstViewPresenter firstViewPresenter();

        void inject(FirstView firstView);

        void inject(FirstViewPresenter firstViewPresenter);
    }

    @Module
    public static class FirstViewModule {
        private int parameter;

        public FirstViewModule(int parameter) {
            this.parameter = parameter;
        }

        @Provides
        public String data(Context context) {
            return context.getString(parameter);
        }

        @Provides
        @ViewScope //needed to preserve scope
        public FirstViewPresenter firstViewPresenter() {
            return new FirstViewPresenter();
        }
    }

    public static class FirstViewPresenter
            extends ViewPresenter<FirstView> {
        public static final String TAG = FirstViewPresenter.class.getSimpleName();

        @Inject
        String data;

        public FirstViewPresenter() {
            Log.d(TAG, "First View Presenter created: " + toString());
        }

        @Override
        protected void onEnterScope(MortarScope scope) {
            super.onEnterScope(scope);
            FirstViewComponent firstViewComponent = scope.getService(DaggerService.TAG);
            firstViewComponent.inject(this);
            Log.d(TAG, "Data [" + data + "] and other objects injected to first presenter.");
        }

        @Override
        protected void onSave(Bundle outState) {
            super.onSave(outState);
            FirstView firstView = getView();
            outState.putString("input", firstView.getInput());
        }

        @Override
        protected void onLoad(Bundle savedInstanceState) {
            super.onLoad(savedInstanceState);
            if(!hasView()) {
                return;
            }
            FirstView firstView = getView();
            if(savedInstanceState != null) { //needed check
                firstView.setInput(savedInstanceState.getString("input"));
            }
        }

        public void goToNextActivity() {
            FirstPath firstPath = Path.get(getView().getContext());
            if(firstPath.parameter != R.string.hello_world) {
                Flow.get(getView()).set(new FirstPath(R.string.hello_world));
            } else {
                Flow.get(getView()).set(new SecondPath());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


pio*_*hen 16

你有一个特定的包,你把所有与Dagger相关的课程放在哪里?

或者您将它们放在他们注入的相关类旁边,例如,如果您有MainActivityModule和MainActivityComponent,则将它们放在与MainActivity相同的包中.

我没有太多经验,但我可以告诉你我的方法.也许一些具有更多经验的人可以改进该解决方案或提供他们的观点.

我经常组织Dagger 2这样的课程:

- di
|
+-- ApplicationComponent class
|    
+-- modules
   |
   +-- AndroidModule class
   |
   +-- WebServiceModule class
   |
   +-- ...
   |
Run Code Online (Sandbox Code Playgroud)
  • di package包含与Dagger 2和依赖注入相关的类.
  • 在大多数情况下,Android应用程序通常有一个组件 - 这里它的名字ApplicationComponent- 我还没有创建一个包含许多Dagger 2组件的Android应用程序,而且我看到只有一个组件的解决方案.
  • modules 包中包含Dagger 2模块

我没有为每个Activity创建模块.模块组特定功能.例如,与SharedPreferences,EventBus(如果您使用类似的东西),网络连接等接口等系统密切相关的元素可能位于AndroidModule.如果您的项目具有WebService的重要接口,或者有很多接口,您可以将它们分组WebServiceModule.例如,如果您的应用程序负责分析网络,并且具有许多与网络相关的类似任务的接口,则可以将这些接口分组NetworkModule.当您的应用程序很简单时,您可能只有一个模块.当它很复杂时,你可以有很多模块.在我看来,你不应该在一个模块中有很多接口.在出现这种情况时,您可以考虑将它们分成不同的模块.您还可以在单​​独的模块中保留一些特定于您的项目的业务逻辑.

另外,我见过很多人将组件定义为内部类,例如在Application类中定义的ApplicationComponent.你认为这是一个好习惯吗?

我不确定这是好事还是坏事.我认为没有必要这样做.您可以get()在类扩展Application类中创建公共静态方法,它将返回Application单例实例.这是一个更简单的解决方案,我们应该只有一个Application类的实例.如果我们想在单元测试中模拟Context,我们可以接受Context作为参数,并在应用程序代码中根据情况传递Application Context或Activity Context.

请注意,这只是我的方法,一些更有经验的开发人员可以以不同的更好的方式组织他们的项目.