gpt4 book ai didi

android - 如何避免将FragmentX绑定(bind)到Fragment

转载 作者:行者123 更新时间:2023-11-29 15:36:21 24 4
gpt4 key购买 nike

如何避免将FragmentX绑定(bind)到Fragment

我有几个文件,在这些文件中我只声明了FragmentXFragment(或ActivityXActivity)的绑定(bind),以便能够将对象作为基类依赖项注入(inject)。

这些文件看起来像这样

@Module
abstract class FragmentXModule {

@Binds
@FragmentScoped
internal abstract fun bindFragment(fragmentX: FragmentX): Fragment
}

这是一遍又一遍地重复。

是否可以避免重复创建这些文件并将所有绑定(bind)分组到一个文件中?

最佳答案

更新:实际上要容易得多!

我写了一个很长的答案,就是如果不复制所有代码,实际上是不可能的,那实际上是不可能的。您可以阅读下面的旧答案以供参考,我将在此处仅包括简单的解决方案。

事实证明,存在AndroidInjector.Factory接口(interface)和AndroidInjector.Builder类是一个很好的理由。我们可以自己实现接口(interface),而使用我们的构建器!这样,我们仍然可以继续使用Dagger Android部件来注入(inject)我们的组件,而无需自己重新创建。

不同的组件可以使用不同的构建器,最后它们只需要实现AndroidInjector.Factory<T>即可。以下构建器显示了绑定(bind)类型和一个父类(super class)型的通用方法。

abstract class SuperBindingAndroidInjectorBuilder<S, T : S> : AndroidInjector.Factory<T> {

override fun create(instance: T): AndroidInjector<T> {
seedInstance(instance)
superInstance(instance)
return build()
}

// bind the object the same way `AndroidInjector.Builder` does
@BindsInstance
abstract fun seedInstance(instance: T)

// _additionally_ bind a super class!
@BindsInstance
abstract fun superInstance(instance: S)

abstract fun build(): AndroidInjector<T>
}

我们可以使用此Builder代替 AndroidInjector.Builder,它也允许我们绑定(bind)父类(super class)型。
@Subcomponent
interface MainActivitySubcomponent : AndroidInjector<MainActivity> {

@Subcomponent.Builder
abstract class Builder : SuperBindingAndroidInjectorBuilder<Activity, MainActivity>()

}

使用上面的构建器,我们可以将我们不想注入(inject)的基本类型与我们将要注入(inject)的实际类型一起声明为我们的第一个类型参数。这样,我们可以毫不费力地提供 ActivityMainActivity

Is it possible to avoid those file creation repeat and group all bindings in one file?



基本上只有2种方法可以为Dagger添加绑定(bind)。一种是您采用的模块方法,它需要添加具有正确绑定(bind)的模块,另一种是将实例直接绑定(bind)到 Component.Builder。 (是的,您还可以向构建器添加一个带有构造函数参数的模块,但这具有相同的效果并导致更多的代码)

如果您没有使用AndroidInjection,但仍在手动创建每个组件,那么您要做的就是将 @BindsInstance abstract fun activity(instance: Activity)添加到 Subcomponent.Builder中,并在构造组件时将其传递进来。如果您想使用AndroidInjection,则我们需要做更多的事情,我将在下面的文章中详细介绍。

在您的特定用例中,我将继续做您现在正在做的事情,但是我将向您展示如何处理此问题的另一种方式。这里的缺点是我们不能再使用 @ContributesAndroidInjectorAndroidInjection.inject() ...

为什么我们不能使用 @ContributesAndroidInjector @ContributesAndroidInjector将为我们生成烦人的可编写样板,但我们需要修改此生成的代码。特别是,我们需要使用组件实现的不同接口(interface),唯一的选择就是自己编写样板。是的,我们当然可以创建自己的AnnotationProcessor来生成所需的样板,但这超出了此答案的范围。

The following part is outdated, but I will leave it as reference to what actually goes on within AndroidInjection. Please use the solution presented above if you want to add additional bindings.



AndroidInjection已经将 SpecificActivity本身绑定(bind)到了图形,但是不允许我们将其视为 Activity。为此,我们将必须使用自己的类并将其绑定(bind)为 Activity。也许Dagger将来会获得这样的功能。

有哪些修改?

我们从 @ContributesAndroidInjector为我们生成的默认设置开始。这是您应该熟悉的一个。 (如果您尚未使用AndroidInjection,请不用担心,我们将在下一步中创建自己的设置)
@Component(modules = [AndroidSupportInjectionModule::class, ActivityModule::class])
interface AppComponent {

fun inject(app: App)

@Component.Builder
interface Builder {
@BindsInstance fun app(app: App) : AppComponent.Builder
fun build(): AppComponent
}
}

@Module(subcomponents = [MainActivitySubcomponent::class])
internal abstract class ActivityModule {
@Binds
@IntoMap
@ActivityKey(MainActivity::class)
internal abstract fun bindMainActivityFactory(builder: MainActivitySubcomponent.Builder): AndroidInjector.Factory<out Activity>
}

@Subcomponent
interface MainActivitySubcomponent : AndroidInjector<MainActivity> {

@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<MainActivity>()

}

通过此设置,我们现在可以安全地注入(inject) MainActivity,甚至将其绑定(bind)到模块中的 Activity上,但这不是我们想要的。我们希望这种绑定(bind)是自动化的。让我们看看是否可以做得更好。

如前所述,我们不能使用 AndroidInjection.inject()。相反,我们需要创建自己的接口(interface)。本着众多Android库的精神,我将我的接口(interface)称为 AwesomeActivityInjector。我将保持简短和简单,但是您可以阅读 AndroidInjector以获取更多信息,而我基本上只是复制了这些信息。

我虽然添加了一个修改。 activity(activity : Activity)也将允许我们将 Activity 绑定(bind)到组件。
interface AwesomeActivityInjector<T : Activity> {

fun inject(instance: T)

interface Factory<T : Activity> {
fun create(instance: T): AwesomeActivityInjector<T>
}

abstract class Builder<T : Activity> : AwesomeActivityInjector.Factory<T> {
override fun create(instance: T): AwesomeActivityInjector<T> {
activity(instance) // bind the activity as well
seedInstance(instance)
return build()
}

@BindsInstance
abstract fun seedInstance(instance: T)

@BindsInstance
abstract fun activity(instance: Activity)

abstract fun build(): AwesomeActivityInjector<T>
}
}

这是我们的组件及其构建器将实现的简单接口(interface),其方式与AndroidInjection当前执行的方式相同。通过在子组件上使用公共(public)接口(interface),我们可以使用它创建组件并注入(inject) Activity 。

调整我们的组件

现在我们有了接口(interface),我们切换出Subcomponent和Module来使用它。这仍然是相同的代码,我只是将 AndroidInjector替换为 AwesomeActivityInjector
@Module(subcomponents = [MainActivitySubcomponent::class])
internal abstract class ActivityModule {
@Binds
@IntoMap
@ActivityKey(MainActivity::class)
internal abstract fun bindMainActivityFactory(builder: MainActivitySubcomponent.Builder): AwesomeActivityInjector.Factory<out Activity>
}

@Subcomponent
interface MainActivitySubcomponent : AwesomeActivityInjector<MainActivity> {

@Subcomponent.Builder
abstract class Builder : AwesomeActivityInjector.Builder<MainActivity>()

}

准备注入(inject)

现在完成所有设置,我们所需要做的就是添加一些代码以注入(inject)我们的Activity。 AndroidInjection部分通过使应用程序实现接口(interface)等来更好地做到这一点。您可以查看如何完成此操作,但是现在我将直接注入(inject)我们的工厂并使用它们。

使用通配符时,请小心使用Dagger和Kotlin!
class App : Application() {

@Inject
lateinit var awesomeInjectors: Map<Class<out Activity>, @JvmSuppressWildcards Provider<AwesomeActivityInjector.Factory<out Activity>>>
}

object AwesomeInjector {

fun inject(activity: Activity) {
val application = activity.application as App

val factoryProviders = application.awesomeInjectors
val provider = factoryProviders[activity.javaClass] as Provider<AwesomeActivityInjector.Factory<out Activity>>

@Suppress("UNCHECKED_CAST")
val factory = provider.get() as AwesomeActivityInjector.Factory<Activity>
factory.create(activity).inject(activity)
}
}

使用我们的 AwesomeActivityInjector
现在,有了所有这些,我们就可以进行工作了。现在我们可以同时注入(inject) ActivityMainActivity
class MainActivity : AppCompatActivity() {

@Inject
lateinit var mainActivity: MainActivity
@Inject
lateinit var activity: Activity

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

AwesomeInjector.inject(this)
}
}

该代码适用于“ Activity ”,并且可以类似地扩展为涵盖片段。

最后的话

是的,这可能是矫kill过正。至少我要说的是,如果我们只想将 MainActivity绑定(bind)为 Activity。我写了这个答案,以举例说明AndroidInjection的工作方式以及如何适应和修改它。

使用类似的方法,您还可以使PerScreen范围可以承受方向更改,或者使UserScope的寿命短于Application,但比Activity长。目前,这一切都无法与AndroidInjection一起使用,并且需要上面显示的自定义代码。

关于android - 如何避免将FragmentX绑定(bind)到Fragment,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49072558/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com