当前位置: 首页 > news >正文

如何用Dagger 2在Kotlin Android应用中实现依赖注入:基于PhotoAffix的完整教程

如何用Dagger 2在Kotlin Android应用中实现依赖注入:基于PhotoAffix的完整教程

【免费下载链接】photo-affix📷 Stitch your photos together vertically or horizontally easily!项目地址: https://gitcode.com/gh_mirrors/ph/photo-affix

PhotoAffix 是一款用 Kotlin 编写的 Android 照片拼接应用,它将 Dagger 2 依赖注入框架运用得恰到好处:通过 1 个 AppComponent 和 5 个 Module,把图片引擎、用户偏好、协程调度器等依赖自动装配到界面层。这篇完整教程带你从核心概念到实战源码,快速掌握 Dagger 2 依赖注入的全部用法。

PhotoAffix 是什么:轻量级照片拼接 Android 应用 📷

PhotoAffix 的玩法很简单:导入手机里的照片,选择横向或纵向排列,调整尺寸与间距,一键拼接成一张大图导出。它采用多模块(multi-module)架构,将界面、引擎、偏好、工具拆分为独立子项目,而这正是引入Dagger 2 依赖注入的典型场景——依赖关系复杂、需要清晰解耦。

项目使用 Dagger 2.21,版本集中声明在 dependencies.gradle 中。

3 分钟理解 Dagger 2 依赖注入核心概念 🧩

在动手之前,先搞懂 Dagger 2 依赖注入的 4 个核心角色:

概念作用一句话理解
@Component依赖容器依赖图的"总指挥",负责组装和分发
@Module依赖来源声明"哪些依赖从哪来"
@Provides手动提供用工厂方法创建实例(适合第三方库)
@Binds绑定别名把实现类绑定到接口上(零成本)

再加上@Inject(声明"我要什么")、@Singleton(单例)和Qualifier 限定符(区分同类型依赖),就构成了完整的 Dagger 2 依赖注入体系。

PhotoAffix 依赖注入架构全解析

PhotoAffix 的依赖注入涉及 7 个关键文件,分布在appprefsengineutilities四个模块中,每个文件各司其职。下面按"搭建依赖注入的 4 个关键步骤"逐步拆解。

第 1 步:创建 AppComponent 依赖容器

一切依赖注入的起点是 AppComponent.kt。它声明了 5 个 Module 作为依赖来源,并列出 3 个需要注入的"消费端":

@Singleton @Component( modules = [ PrefsModule::class, AppBindModule::class, UtilityModule::class, EnginesModule::class, AppProvideModule::class ] ) interface AppComponent { fun inject(mainActivity: MainActivity) fun inject(settingsLayout: SettingsLayout) fun inject(imageSpacingDialog: ImageSpacingDialog) @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder fun build(): AppComponent } }

💡要点@Component.Builder中的@BindsInstance application(...)是"入口参数"——Application 实例由外部传入,成为整个依赖图的根基。

第 2 步:在 Application 启动时构建组件

App.kt 在应用启动的一瞬间构建 Dagger 组件:

appComponent = DaggerAppComponent.builder() .application(this) .build()

注意类名DaggerAppComponentDagger 编译器自动生成的,无需手写。App 还实现了 Injector.kt 中的injectInto(target)接口,通过when (target)把不同对象分发到对应的inject()方法,让注入入口统一收口。

第 3 步:用 Module 划分职责边界

PhotoAffix 的 5 个 Module 恰好展示了两种提供依赖的方式:

@Binds:接口绑定实现类

AppBindModule.kt、EnginesModule.kt、UtilityModule.kt 都遵循同一模式——把Real*实现绑定到接口:

@Binds @Singleton abstract fun providePhotoLoader(realPhotoLoader: RealPhotoLoader): PhotoLoader

抽象方法(abstract fun)+@Binds意味着:实例由 Dagger 自动构造,你只需要告诉它"谁实现谁"。

@Provides:手动创建复杂实例

PrefsModule.kt 和 AppProvideModule.kt 则适合需要参数或特殊逻辑的场景:

@Provides @Singleton fun provideRxkPrefs(app: Application): RxkPrefs { return rxkPrefs(app, KEY_PREFERENCES) }

AppProvideModule 还提供了一个巧妙用法——把协程调度器也交给 Dagger 管理:

@Provides @Singleton @MainDispatcher fun provideMainDispatcher(): CoroutineContext = Dispatchers.Main @Provides @Singleton @IoDispatcher fun provideIoDispatcher(): CoroutineContext = Dispatchers.IO

第 4 步:消费端接收注入

看 MainActivity.kt 如何"伸手要依赖":

@Inject lateinit var presenter: MainPresenter @Inject lateinit var affixEngine: AffixEngine override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) (application as App).appComponent.inject(this) // 触发注入 ... }

MainPresenter的实现类 MainPresenter.kt 通过@Inject构造函数自动装配依赖,无需任何new操作:

class RealMainPresenter @Inject constructor( private val ioManager: IoManager, private val photoLoader: PhotoLoader ) : MainPresenter

🎯 这就是依赖注入的魅力:MainActivity 只依赖接口,具体实现由 Dagger 2 在运行时决定——想替换实现或写单元测试,改 Module 即可,业务代码零改动。

Qualifier 限定符:如何区分同类型依赖 🔍

当同一类型有多个实例时,Dagger 需要"名牌"来区分。PrefQualifiers.kt 定义了 5 个自定义限定符:

@Qualifier @Retention(RUNTIME) annotation class ImageSpacingVertical

每个Pref<Int>类型都长一样,但加上@ImageSpacingVertical@ImageSpacingHorizontal@StackHorizontally等限定符后,Dagger 就能精确区分"纵向间距"和"横向间距"这两个完全不同的偏好。同样的手法也用于 IoDispatcher.kt 区分 Main 与 IO 调度器。

⚠️新手注意:自定义限定符必须同时加@Qualifier@Retention(RUNTIME),少写一个都会导致编译或运行异常。

新手常见 3 个疑问 ❓

Q1:DaggerAppComponent 是谁生成的?Dagger 的 Gradle 插件(com.google.dagger)在编译期扫描@Component接口,自动生成带Dagger前缀的类。你只需编写声明,绝不该手写生成代码。

Q2:@Binds@Provides怎么选?简单规则:实现类可以直接new出来 → 用@Binds(更简洁高效);需要参数、条件逻辑或包装第三方类 → 用@Provides。PhotoAffix 两种都用到了。

Q3:为什么用lateinit var接收注入?@Inject lateinit是字段注入的标准写法,Dagger 会在inject(this)调用后填充值。缺点是绕过编译器检查,更严格的团队会改用构造函数注入(如RealMainPresenter的写法)。

总结:PhotoAffix 的 Dagger 2 依赖注入架构亮点 ✅

亮点说明
单一 Component整个应用只需一个 AppComponent,管理简单
Module 按模块拆分每个子模块自带 Module,解耦彻底
双轨提供方式@Binds管接口实现,@Provides管复杂构造
Qualifier 规范化同类型依赖用限定符区分,语义清晰
协程调度器注入化Dispatchers 也纳入依赖图,替换测试更方便

从 PhotoAffix 的这套 Dagger 2 依赖注入实践中可以看出:依赖注入不是炫技,而是当你的 Android 应用模块越来越多、依赖关系越来越复杂时,让代码"可组装、可测试、可替换"的基础设施。建议直接对照上述 7 个源码文件跟读一遍,Dagger 2 依赖注入从此不再陌生。

【免费下载链接】photo-affix📷 Stitch your photos together vertically or horizontally easily!项目地址: https://gitcode.com/gh_mirrors/ph/photo-affix

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/4226380.html

相关文章:

  • fb_graph认证全解析:Cookie解析与Signed Request的HMAC-SHA256验证原理指南
  • 为什么 Ornith-1.5-397B 效果忽好忽坏?temperature、top_p、top_k 采样参数调优完整指南
  • 5分钟上手psr/clock:从composer安装到第一个now()调用的快速教程
  • IntelliJ IDEA + phpStudy + ApiPost断点调试
  • 部署 FN-DSA 前:你必须了解的风险与权衡
  • 2026实测!梦琪科技拨号VPS排行,这3款性价比封神
  • 多模态模型也能当纯文本 LLM:InternVL3-2B-hf 无图文本生成快速入门
  • 公司电脑权限怎么管理?从账号、文件到软件,一套权限管理思路讲清楚
  • Status 拆解:19 天百万用户的 AI 社交模拟器如何炼成
  • Video2X AI 视频放大与补帧完整入门指南:从 480p 到 4K 成片
  • 华为MetaERP # EBS → Fusion BC 预算模块【业务规则对齐实施指南】定位:迁移蓝图「业务规则映射章节」标准内容,承接主数据映射、余额迁移、预算校验逻辑迁移。> > 核心结
  • PDF补丁丁:一个免费的PDF工具箱,搞定书签、合并、提图等8类编辑任务
  • 如何在React Native中使用react-timer-hook?移动App倒计时与秒表组件跨平台实战教程
  • 自己设计一道数独:LibreSudoku自定义出题功能完整流程演示
  • 基于SpringBoot的网购优选服务系统设计网站(源码+lw+部署文档+讲解等)
  • 反向工程nest-router源码:forRoutes背后的MODULE_PATH元数据魔法
  • 基于SpringBoot的甜品店在线点餐及预约系统毕业设计项目源码文档
  • 创维SK-L1002灵犀屏深度优化指南:免刷机安装第三方桌面,秒变流畅安卓机顶盒|手机一键操作|纯技术教程
  • ip-location-zh 完全指南:PHP 如何 3 分钟搞定无需数据库的 IP 地理位置查询?
  • MinIO 停止维护怎么办?Docker 迁移 RustFS 实战:数据零丢失
  • LX Music 桌面版:免费音乐聚合播放器完整指南
  • Outfit 字体完整指南:9 种字重免费商用,3 分钟装进系统
  • Shallows序列化能力清单:JSON、Plist与String存储映射全覆盖指南
  • 数字化转型全景解析:标准化、信息化、数字化与智能化的演进路径与实践指南
  • 为什么Intel放弃Hyperscan:Vectorscan项目背后的开源反击战
  • Java面试前,我建议你重点复习这几个核心知识点
  • 谷歌数据分析 V 笔记(二)
  • 2026年口碑出众的表型平台生产厂家盘点,值得你深入了解
  • 3步完成微信聊天记录导出全指南:免费永久保存为HTML和Word
  • 如何用 Cosmic Ray 的 cr-rate 诊断测试薄弱点:变异存活率分析完全教程