Android模块化项目集成Dagger 2.11的组件设计问题求助
Hey there! I’ve tackled this exact problem when building modular Android apps with Dagger, so I know how tricky it can feel to get right. The key is to let each module own its Dagger setup while keeping it hooked into the app’s overall dependency graph. Let’s break this down step by step with concrete examples.
Core Principles to Keep in Mind
First, a quick sanity check: each module should encapsulate its own Dagger components/subcomponents and modules, only exposing the dependencies other modules or the app layer actually need. No leaking internal implementation details!
Step 1: Define Module-Specific Dagger Components/Subcomponents
Most of the time, you’ll want to use Subcomponents for feature modules because they can inherit dependencies from your app’s root AppComponent (like network clients, database instances, etc.). If a module is completely independent (rare), you can use a standalone @Component instead.
Let’s use a feature-profile module as an example:
// Inside feature-profile module @Subcomponent(modules = [ProfileModule::class]) @ProfileScope // Custom scope for this module (more on this later) interface ProfileComponent { // Expose only the dependencies other parts of the app need fun profileViewModelFactory(): ProfileViewModelFactory // Factory to create instances of this subcomponent @Subcomponent.Factory interface Factory { fun create(): ProfileComponent } } // Custom scope to ensure singleton-like behavior within the component's lifecycle @Scope @Retention(AnnotationRetention.RUNTIME) annotation class ProfileScope
Step 2: Create Module-Level Dagger Modules
Each module will have its own Dagger @Module to provide internal dependencies. This keeps all module-specific dependency logic contained:
// Inside feature-profile module @Module class ProfileModule { // Provide internal repository implementation @Provides @ProfileScope fun provideProfileRepository(apiService: ApiService): ProfileRepository { return ProfileRepositoryImpl(apiService) } // Expose a ViewModel factory that other components can use @Provides @ProfileScope fun provideProfileViewModelFactory(repo: ProfileRepository): ProfileViewModelFactory { return ProfileViewModelFactory(repo) } }
Notice we’re using our custom @ProfileScope here—this ensures dependencies are reused within the ProfileComponent lifecycle (e.g., as long as the Profile screen is active), without making them app-wide singletons.
Step 3: Hook Subcomponents into the Root AppComponent
Your app’s root AppComponent needs to know about the subcomponents so other parts of the app can create instances of them. We do this by exposing the subcomponent’s factory:
// Inside app module @Component(modules = [AppModule::class]) @AppScope interface AppComponent { // Expose the factory for ProfileComponent fun profileComponentFactory(): ProfileComponent.Factory // Expose app-wide dependencies that subcomponents might need (like ApiService) fun apiService(): ApiService }
Step 4: Initialize the Component in Your Module
Now, inside your feature module’s entry point (like a Fragment or Activity), you’ll retrieve the AppComponent and use its factory to create your module’s component:
// Inside feature-profile module's ProfileFragment class ProfileFragment : Fragment() { private lateinit var viewModel: ProfileViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Get the root AppComponent (usually stored in your Application class) val appComponent = (requireContext().applicationContext as MyApp).appComponent // Create the ProfileComponent using the factory from AppComponent val profileComponent = appComponent.profileComponentFactory().create() // Retrieve the exposed ViewModel factory and initialize the ViewModel val viewModelFactory = profileComponent.profileViewModelFactory() viewModel = ViewModelProvider(this, viewModelFactory)[ProfileViewModel::class.java] } }
Step 5: Handle Cross-Module Dependencies (If Needed)
If Module A needs a dependency from Module B, don’t let Module A depend directly on Module B’s Dagger components. Instead:
- Create a shared interface in a base module (like
core) that defines the dependency. - Have Module B’s component expose that interface.
- Module A can then request the interface from the
AppComponent(which gets it from Module B’s component).
This keeps your modules decoupled and avoids circular dependencies.
Common Pitfalls to Avoid
- Over-scoping: Don’t use
@Singletonfor module-specific dependencies—use custom scopes like@ProfileScopeinstead to keep instances tied to the module’s lifecycle. - Leaking internal details: Only expose what’s necessary. For example, don’t expose
ProfileRepositoryImpl—expose theProfileRepositoryinterface instead. - Circular dependencies: Ensure module dependencies are one-way (e.g.,
feature-profiledepends oncore, butcoredoesn’t depend onfeature-profile).
内容的提问来源于stack exchange,提问作者AouledIssa

