(来自/ r / androiddev的x-
post
)
我想以说这不是“更好”的帖子作为开头。严格来说,这是一个关于如何使用Dagger构建东西(以及如何在Kodein中构建它以帮助说明问题)的问题。
我已经在多个工作项目中使用Kodein几年了,但是我发现它使用起来非常容易,以至于我再也不会看过Dagger了。我开始了一个新的个人项目,我想再给Dagger做个尝试。
为简单起见,我有3个模块(这是一个常规的桌面应用程序,不是Android的)。
app
包含一个类App
:
class App(
private val api: GoogleApi,
private val argParser: ArgParser
) {
fun run() {
while(true) {
api.login(argParser.username, argParser.password);
}
}
}
common
包含一个类ArgParser
(实现并不重要)
google
包含几个类:
class GoogleApi(
driveProvider: () -> Drive
) {
private val drive by lazy {
driveProvider()
}
fun login(username: String, password: String) {
drive.login() // not real call
}
}
internal class CredentialRetriever(
private val transport: NetHttpTransport,
private val jsonFactory: JacksonFactory
) {
fun retrieveCredentials() = ...
}
的依赖项google
是:
dependencies {
implementation "com.google.api-client:google-api-client:$googleApiVersion"
implementation "com.google.oauth-client:google-oauth-client-jetty:$googleApiVersion"
implementation "com.google.apis:google-api-services-drive:v3-rev110-$googleApiVersion"
}
我implementation
之所以专门使用,是因为我不希望任何人直接使用基础Google库。
为了使它在Kodein中起作用,我在以下步骤中进行了以下操作main
:
fun main(args: Array<String>) {
val kodein = Kodein {
import(commonModule(args = args))
import(googleModule)
import(appModule)
bind<App>() with singleton {
App(
api = instance(),
argParser = instance()
)
}
}
kodein.direct.instance<App>().run()
}
然后在google
:
val googleModule = Kodein.Module("Google") {
bind<CredentialRetriever>() with provider {
CredentialRetriever(jsonFactory = instance(), transport = instance())
}
bind<Drive>() with provider {
Drive.Builder(
instance(),
instance(),
instance<CredentialRetriever>().retrieveCredentials()
).setApplicationName("Worker").build()
}
bind<GoogleApi>() with singleton {
GoogleApi(drive = provider())
}
bind<JacksonFactory>() with provider {
JacksonFactory.getDefaultInstance()
}
bind<NetHttpTransport>() with provider{
GoogleNetHttpTransport.newTrustedTransport()
}
}
最后在common
:
fun commonModule(args: Array<String>) = Kodein.Module("Common") {
bind<ArgParser>() with singleton { ArgParser(args = args) }
}
我尝试在Dagger中实现此功能,但无法正常工作。我的第一个尝试是让Component
in
app
依赖于common
和的模块google
。这不起作用,因为生成的代码引用了未从google
(如Drive
)公开的类。我可以通过使它们具有api
依赖性来解决此问题,但是我不想公开它们:
// CredentialRetriever and GoogleApi were updated to have @Inject constructors
// GoogleApi also got an @Singleton
@Module
object GoogleModule {
@Provides
internal fun drive(
transport: NetHttpTransport,
jsonFactory: JacksonFactory,
credentialRetriever: CredentialRetreiver
): Drive =
Drive.Builder(
transport,
jsonFactory,
credentialRetriever.retrieveCredentials()
).setApplicationName("Worker").build()
@Provides
internal fun jsonFactory(): JacksonFactory =
JacksonFactory.getDefaultInstance()
@Provides
internal fun netHttpTransport(): NetHttpTransport =
GoogleNetHttpTransport.newTrustedTransport()
}
接下来,我尝试为每个模块制作组件(渐变模块):
// in google module
@Singleton
@Component(modules = [GoogleModule::class])
interface GoogleComponent {
fun googleApi(): GoogleApi
}
// in common module
@Singleton
@Component(modules = [CommonModule::class])
interface CommonComponent {
fun argParser(): ArgParser
}
然后在app
好玩的开始:
// results in "AppComponent (unscoped) cannot depend on scoped components:"
@Component(dependencies = [CommonComponent::class, GoogleComponent::class])
interface AppComponent {
fun app(): App
}
好的,让我们对其进行范围界定:
// results in "This @Singleton component cannot depend on scoped components:"
@Singleton
@Component(dependencies = [CommonComponent::class ,GoogleComponent::class])
interface AppComponent {
fun app(): App
}
编辑 :尝试AppComponent
使用自定义范围:
// results in "AppComponent depends on more than one scoped component:"
@AppScope
@Component(dependencies = [CommonComponent::class ,GoogleComponent::class])
interface AppComponent {
fun app(): App
}
如何在Dagger中实现呢?我已经阅读了文档,我认为我对它们有些了解,但是我不知道下一步该怎么做。
我可以自由地将示例更改为:a)删除不必要的细节,b)简化设置。
给出3个具有以下类别的模块:
// ----->> app <<-----
class App @Inject constructor(
private val api: AbstractApi,
private val argParser: ArgParser
)
// ----->> google <<-----
// expose a public interface
interface AbstractApi
// have our internal implementation
internal class GoogleApi @Inject constructor(
private val argParser: ArgParser
) : AbstractApi
// ----->> common <<-----
// expose some common class
interface ArgParser
因此,我们需要绑定一个实现了ArgParser
两个google
和app
。我ArgParser
在这里以示例为例,说明如何将参数传递给API。GoogleApi
完全internal
确保没有泄漏。我们只公开接口AbstractApi
。
我通过GoogleApi
内部实现来消除实现/
API的Gradle复杂性。行为是相同的,甚至可能更严格:我们的模块中有一些我们无法公开的类。这样,我们也可以进行编译器验证。
我们可以将所有实现细节都隐藏在添加的组件后面,以添加组件google
以创建GoogleApi
接口的实现。
// ----->> google
@Component(modules = [ApiModules::class])
interface ApiComponent {
// has a provision method for our API
fun api(): AbstractApi
@Component.Factory
interface Factory {
// factory method to bind additional args that we need to supply
fun create(@BindsInstance parser: ArgParser): ApiComponent
}
}
@Module
internal interface ApiModules {
@Binds
fun bindApi(googleApi: GoogleApi): AbstractApi
}
我们在这里不使用范围,因为应该在使用此组件的任何地方处理范围。ArgParser
是我们可能需要提供以创建对象的参数的示例。我们也可以使用a
@Component.Builder
代替工厂。
Dagger将在同一模块(google
)中生成组件,因此不会出现有关引用代码的任何问题。我们要做的就是在我们的app
模块中检索API :
// ----->> app
@Component(modules = [AppModule::class])
interface AppComponent {
fun app(): App
}
@Module
class AppModule {
@Provides
fun provideParser(): ArgParser = object : ArgParser {} // just bind a dummy implementation
@Provides
fun provideApi(argParser: ArgParser): AbstractApi {
return DaggerApiComponent.factory().create(argParser).api()
}
}
现在,我们可以使用组件工厂从模块中创建实例。如果我们需要一个范围,我们可以像往常一样在@Provides
方法上添加它。
此设置应完全隐藏app
公共接口后面模块中的所有细节。生成的代码位于同一模块内。
@Module
?一个@Subcomponent
?据报道,将模块添加到组件中也会在该组件内生成工厂代码,这将尝试使用未引用的类。子组件也是如此。
由于组件上没有作用域,因此我们最好将其添加为组件依赖项,但那时我们将无法添加作用域。另外,传递参数会很麻烦,因为在创建组件时必须提供参数。
问题内容: 在Windows 7上,我已按以下说明安装了gulp:http : //markgoodyear.com/2014/01/getting-started-with- gulp/ : 在我的应用文件夹中: 我创建一个文件。 但是,当我尝试运行时,出现以下错误消息: 等等 但是存在于(在本地应用程序文件夹中): 知道可能是什么原因吗? 问题答案: 更新 从更高版本开始,无需手动安装gulp
我想创建一个运行多个python项目的脚本。每个项目都是阻塞的(意味着每个项目都应该永远运行),所以这个脚本必须并行运行每个项目。此外,我希望在关闭主shell之后,所有这些进程都关闭。这是我想出的shell代码 我尝试使用&使python进程异步运行,但是脚本在第二行之后没有继续,只有第一个项目在运行。 有没有一种方法可以运行所有的python进程?
问题内容: 我正在尝试在Google App Engine中使用多个模块。 我尝试使用此源代码: https://github.com/GoogleCloudPlatform/appengine-modules-helloworld- python 但是我似乎找不到从所有.yaml加载的pycharm运行的方法,因此似乎只有app.yaml加载了。 另外,当尝试将代码上传到Appengine(使用
问题内容: 我正在尝试创建一个使用设置和Gui模块的用户系统,并且当GUI模块请求使用pickle加载文件时,我总是遇到属性错误。这来自设置模块: 这是GUI模块: 每个用户都是一个类,并放入列表中,然后当我仅加载设置文件并验证登录名时,使用pickle保存该列表,一切正常,但是当我打开GUI模块并尝试验证其是否无效时让我,我得到的错误: 问题答案: 问题在于,您 实际上是通过运行“设置”模块 来
我这里有一个多模块maven项目。 父项目共有3个模块,with-paranamer,with-paranamer。 下面是项目的结构。 我想使用exec-maven-plugin在withon-paranamer模块中执行类。因此,我在这里的parent pom.xml中的pluginManagement下添加了exec-maven-plugin。 在unit-paranamer模块中,我添加了
问题内容: 在这里,我需要同时执行,并在同一时间。 当我尝试在其上放置一个并行块时,由于在官方站点中这样提到,因此它引发了错误。 } 问题答案: 您不必将每个调用都放在阶段内的并行作业中,因此可以这样进行: