在Android上编写模块化项目(翻译)
来源博客:Wang Jie's Blog
本文链接:http://blog.wangjiegulu.com/2018/02/13/writing_a_modular_project_on_android
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处。
原文:https://medium.com/mindorks/writing-a-modular-project-on-android-304f3b09cb37
在Android上编写模块化项目(翻译)
当我们在 Android Studio 上创建一个新的项目时,自带一个 app
module。这时我们大多数人编写整个应用的地方。每次点击 run
按钮都会触发我们整个所有 module 上的 gradle 构建,并检查所有文件是否有变化。这就是为什么 gradle 构建会在更大的应用程序上花费 10分钟的时间,并且减慢开发者的输出。
要解决这个问题,复杂的应用程序,如 Uber 决定对它们的应用程序进行模块化并从中获得了很多。下面是试用模块化项目的一些优势:
- 更快的 gradle 构建
- 跨应用/模块复用通用的功能
- 易于插拔到Instant apps
- 更好的团队工作,一个人可以单独负责一个模块
- 更流畅地git flows
由于上述优势,当我刚开始Posts这个应用时,我就在始终坚持使用模块化方法。对此,Android 团队已经给我们提供了一些工具,但是我确实遇到了一些障碍,一下是我学习到的内容:
我该怎么分割我的 modules ?
你的应用程序是流程集构成的,比如,Google Play 有应用详情流,它包含了简要,描述详情,应用截图,评论活动等。
所有这些都可以归为同一模块 —— app-details
。
你的应用会包含多个类似流程的模块,有 authentication
, settings
, on-boarding
等等。当然还有一些不需要UI元素呈现的模块如 —— notifications
, analytics
, first-fetch
等等。这些模块包含与流程有关的 activities, repositories, entities和依赖注入相关东西。
但是这些模块中总是有一些共同的功能和工具。这就是为什么你需要一个 core 模块。
什么是 core 模块 ?
Core
模块是一个你项目中简单的 module 库。core 库可以(除其它外),
- 给你的依赖注入框架提供全局依赖,如 Retrofit, SharedPreferences等等。
- 包含工具类和扩展方法
- 包含全局类和回调
- 在 application 类中的初始化库,如 Firebase Analytics,Crashlytics,LeakCanary,Stetho等等
怎么使用第三方库?
核心(core
)模块的其中一个职责是为你的功能(feature
)模块提供外部依赖。这使得很容易实现在你的 feature
模块中共享相同版本的库。只需要在你的 core
模块的 dependencies 中使用 api
,这样你就能在所有 feature
模块中使用它们。
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
api deps.support.appCompat
api deps.support.recyclerView
api deps.support.cardView
api deps.support.support
api deps.support.designSupport
api deps.android.lifecycleExt
api deps.android.lifecycleCommon
api deps.android.roomRuntime
api deps.android.roomRx
api deps.kotlin.stdlib
api deps.reactivex.rxJava
api deps.reactivex.rxAndroid
api deps.google.dagger
kapt deps.google.daggerProcessor
api deps.square.picasso
api deps.square.okhttpDownloader
api deps.square.retrofit
api deps.square.okhttp
api deps.square.gsonConverter
api deps.square.retrofitRxAdapter
implementation deps.facebook.stetho
implementation deps.facebook.networkInterceptor
testApi deps.test.junit
androidTestApi deps.test.testRunner
androidTestApi deps.test.espressoCore
}
有种依赖的可能性是只有对 feature-a
模块有用,但是在 feature-b
中无用。对于这种情况,我推荐在你的 core 的依赖中使用 api
,因为 proguard 注意到而不会包含在 feature-b
instant app 中。
怎么使用 Room ?
这个困扰我挺久的时间。我们希望把我们的数据库定义到 core
模块中,因为它是我们应用程序要共享的通用的功能。为了让 Room 工作,你需要一个包含了所有 entity 类的数据库文件。
@Database(entities = [Post::class, User::class, Comment::class], version = 1,exportSchema = false)
abstract class PostDb : RoomDatabase() {
abstract fun postDao(): PostDao
abstract fun userDao(): UserDao
abstract fun commentDao(): CommentDao
}
但是,如上面提到的,我们的 entity 类是被定义在 feature
模块中,而且 core
模块不能去访问它们。这是我碰到障碍的地方,经过一番思考后,你做了一件最棒的事,寻求 Yigit 的帮助。
Yigit 阐明了观点,你必须要在每个 feature
模块中都创建一个新的 db 文件,然后每个模块一个数据库。
这有几个好处:
- 迁移是模块化的
- 即时 app 仅包含它们需要的表
- 查询会更快
缺点:
- 跨模块数据关系将不可能
注意:为了 Room 的注解能够工作,不要忘记在你的 feature
模块中增加下面依赖
kapt "android.arch.persistence.room:compiler:${versions.room}"
怎么使用 Dagger 2 ?
同样的问题 Dagger 也遇到了。我的 core 模块中的 application 类不能访问和初始化我 feature
模块中的组件。这是从属组件完美的用例。
你的 core 组件定义了它想要暴露给依赖组件的依赖关系
@Singleton
@Component(modules = [AppModule::class, NetworkModule::class, StorageModule::class, ImageModule::class])
interface CoreComponent {
fun context(): Context
fun retrofit(): Retrofit
fun picasso(): Picasso
fun sharedPreferences(): SharedPreferences
fun scheduler(): Scheduler
}
您的模块组件将 CoreComponent
定义为依赖项,并使用传递的依赖
@ListScope
@Component(dependencies = [CoreComponent::class], modules = [ListModule::class])
interface ListComponent {
fun inject(listActivity: ListActivity)
}
@Module
@ListScope
class ListModule {
/*Uses parent's provided dependencies like Picasso, Context and Retrofit*/
@Provides
@ListScope
fun adapter(picasso: Picasso): ListAdapter = ListAdapter(picasso)
@Provides
@ListScope
fun postDb(context: Context): PostDb = Room.databaseBuilder(context, PostDb::class.java, Constants.Posts.DB_NAME).build()
@Provides
@ListScope
fun postService(retrofit: Retrofit): PostService = retrofit.create(PostService::class.java)
}
在哪里初始化我的 components ?
我为我的功能的所有组件创建了一个单例 holder。这个 holder 用于创建,维护和销毁我的 component 实例。
@Singleton
object PostDH {
private var listComponent: ListComponent? = null
fun listComponent(): ListComponent {
if (listComponent == null)
listComponent = DaggerListComponent.builder().coreComponent(CoreApp.coreComponent).build()
return listComponent as ListComponent
}
fun destroyListComponent() {
listComponent = null
}
}
注意:为了 Dagger 的注解能够工作,不要忘记在你的 feature
模块中增加下面依赖
kapt "com.google.dagger:dagger-compiler:${versions.dagger}"
总结
尽管把你的单独的 application 转成模块化有一些棘手,其中一些我试图通过上面的方法来解决,优点是深刻的。如果您在模块中遇到任何障碍,请随时在下面提及它们,我们可以一起讨论解决方案。
谢谢。
在Android上编写模块化项目(翻译)的更多相关文章
- Android 上传开源项目到 jcenter 实战踩坑之路
本文微信公众号「AndroidTraveler」首发. 背景 其实 Android 上传开源项目到 jcenter 并不是一件新鲜事,网上也有很多文章. 包括我本人在将开源项目上传到 jcenter ...
- 如何在Android上编写高效的Java代码
转自:http://www.ituring.com.cn/article/177180 作者/ Erik Hellman Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架 ...
- 在Eclipse上编写Go项目
Note for users in China Note: if you are behind the Great Firewall of China, you are very likely to ...
- 在Ubuntu上为Android系统编写Linux内核驱动程序(老罗学习笔记1)
这里,我们不会为真实的硬件设备编写内核驱动程序.为了方便描述为Android系统编写内核驱动程序的过程,我们使用一个虚拟的硬件设备,这个设备只有一个4字节的寄存器,它可读可写.想起我们第一次学习程序语 ...
- 利用Android Studio编写 Android上的c与c++程序
利用Android Studio编写 Android上的c与c++程序 (2017-05-22 19:01:20) 转载▼ 标签: android 分类: Android开发 原文链接: http:/ ...
- GitHub上最著名的Android播放器开源项目大全
GitHub上最著名的Android播放器开源项目大全 ...
- 在Ubuntu上为Android系统编写Linux内核驱动程序
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6568411 在智能手机时代,每个品牌的手机都有 ...
- GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。
1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...
- android git上开源的项目收藏
本文为那些不错的Android开源项目第一篇--个性化控件(View)篇,主要介绍Android上那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Ga ...
随机推荐
- Equals()和GetHashCode()方法深入了解
最近在看Jeffrey Richter的CLR Via C#,在看到GetHashCode()方法的时候,有一个地方不是特别明白,就是重写Equals()方法时为什么要把GetHashCode()方法 ...
- [国嵌笔记][021-022][ARM处理器工作模式]
[ARM处理器工作模式] 处理器工作模式 1.User(urs):用户模式,linux应用程序运行在用户模式 2.FIQ(fiq):快速中断模式 3.IRQ(irq):中断模式 4.Superviso ...
- Docker+Jenkins持续集成环境(4):使用etcd+confd实现容器服务注册与发现
前面我们已经通过jenkins+docker搭建了基本的持续集成环境,实现了服务的自动构建和部署,但是,我们遇到一个问题,jenkins构建出来的镜像部署后,需要通过ip:port去访问,有什么更好的 ...
- input事件中文触发多次问题研究
我们在网页中经常会遇到实时搜索的情况,或者其他类似需要input实时响应的问题,一般情况下,我们是利用input和propertychange事件来监听input内容的变化来响应,但是有一个问题就是当 ...
- 关于Struts传递json给easyui的随笔
今天在公司写测试代码,由于公司用的是ssh框架做的商城项目,我想先实现下简单的增删改查,奈何没有很好的后台页面(毕竟不能测试代码直接在他的项目里改啊) 所以想到了淘淘商城中有这个后台的管理页面,打算一 ...
- Linux/Unix系统SSH远程按Backspace键删除时出现^H的处理方法
在linux/unix系统中连接SSH远程工作时,输出字符后按Backspace键删除时,会出现^H,这对习惯了按Backspace键删除的用户来说,感觉非常别扭,虽然可以通过Ctrl+Backspa ...
- As a Start - 毫厘之间,宇宙之外
序 突然想聊聊多重宇宙——多重宇宙,这听上去像是一个科幻概念,但是严肃思考这一个问题时,将会带我们进入一个全新的格局和世界. 对宇宙学家而言,研究多重宇宙并不仅仅是为了猜测平行世界里某一历史事件是否有 ...
- android项目红色感叹号
Project --> Clean 清理一下,一般要注意的,如果是你的项目文件有错误,特别是xml文件,清理后那个R资源文件会不见的,那就需要你把错误修正后自动生成的.
- c指针作为参数传递以及指针的指针
指针作为函数参数传递 函数参数传递的只能是数值,所以当指针作为函数参数传递时,传递的是指针的值,而不是地址. #include "stdio.h" void pointer(int ...
- ASP.net core 2.0.0 中 asp.net identity 2.0.0 的基本使用(二)—启用用户管理
修改和启用默认的用户账户管理和角色管理 一.修改Models目录中的ApplicationUser.cs类文件,如下 namespace xxxx.Models{ //将应用程序用户的属性添加到 ...