Dagger 2 不注入 sharedPreference

Posted

技术标签:

【中文标题】Dagger 2 不注入 sharedPreference【英文标题】:Dagger 2 not injecting sharedPreference 【发布时间】:2018-06-13 09:26:41 【问题描述】:

您好,我是 dagger 2 的新手,并尝试在下面的 MyActivity 类中注入 sharedPreference 的实例:

class MyApplication : Application() 

    companion object 
        @JvmStatic lateinit var applicationComponent : ApplicationComponent
    



    override fun onCreate() 
        super.onCreate()
        applicationComponent = DaggerApplicationComponent.builder().androidModule(AndroidModule(this)).build()

    

这里是组件和模块

@Singleton
@Component(modules = arrayOf(AndroidModule::class))
interface ApplicationComponent 
    fun inject(mainActivity: MainActivity)


@Module
class AndroidModule (private val application: Application) 

    @Provides
    @Singleton
    fun provideApplicationContext() : Context = application

    @Provides
    @Singleton
    fun provideSharedPreference() : SharedPreferences = application.getSharedPreferences("shared pref", Context.MODE_PRIVATE)




class MainActivity: Activity
    @Inject
    internal lateinit var sharedPreference: SharedPreferences

    @Inject
    internal lateinit var MainScreenPresenter: MainScreenContract.Presenter

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_Screen)
        MyApplication.applicationComponent.inject(this)

        sharedPreference.toString()

        initiateViews()
    


我收到以下错误:

Error:(7, 1) error: android.content.SharedPreferences cannot be provided without an @Provides- or @Produces-annotated method.

【问题讨论】:

fun inject(any: Any) 你必须是具体的。那是fun inject(app: MyApplication) 如果我想注入其他 kotlin 类怎么办?我希望能够在项目中注入任何需要依赖注入的类 这不是 DI 的工作方式。从哲学上讲,这是关于做出有意识的决定。类似于“是的,我知道这个组件可以注入这个类”的东西。从技术上讲,组件需要一个要注入的类列表。这些类肯定会成为最终消费者,例如活动或应用程序,这是由 Android 为您创建的,您在创建后将其注入。除此之外,您将使用 @Inject 带注释的构造函数。遵循官方用户指南。 所以如果我创建了一个名为 Myclass 的类,我只需要编辑组件并添加 fun inject(myclass : Myclass )? 你只需要手动注入你自己不调用构造函数的类,比如Activity。如果您创建了一个名为Myclass 的类,您通常会使用构造函数注入来注入其字段。在这里阅读:google.github.io/dagger/users-guide 然后要获得一个实例,您需要 1)在 Activity 或 Application(或其他由 Android 实例化的类)中声明属性 @Inject lateinit var myclass: MyClass,以便可以注入 2)再次使用构造函数注入。 【参考方案1】:

你做的有点不对。首先现在有 dagger-android 帮助解决了组件(例如活动)不应该知道注入是如何发生的问题的原理问题。

你可以在这里阅读:https://medium.com/@iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe

只是为了确保 dagger 依赖项在 Android 项目中:

android 
     kapt 
        generateStubs = true
    


compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile "com.google.dagger:dagger-android-support:2.13"
kapt "com.google.dagger:dagger-compiler:2.13"
kapt "com.google.dagger:dagger-android-processor:2.13"

你的错误是你没有告诉你的图表你想注入 MainActivity。最好的方法是为 MainActivity 创建Subcomponent,将它与另一个具有要注入 MainActivity 的注入的 MainActivity 模块连接,在 AppComponent 中设置与 Subcomponent 的连接,并且仅在 MainAcitivy onCreate() 方法注入你的依赖关系图。但是有了 dagger-android,一切都变得简单了。

代码如下:

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    ActivityBindingModule::class,
    AppModule::class
])
interface AppComponent : AndroidInjector<DaggerApplication> 

    fun inject(application: MyApplication)

    override fun inject(instance: DaggerApplication)

    @Component.Builder
    interface Builder 
        @BindsInstance fun application(application: MyApplication): Builder
        fun build(): AppComponent
    

AndroidSupportInjectionModule.class :这来自 dagger.android.support 库。它为我们的模块提供了 Android 组件(Activity/Fragments/Services/BroadcastReceiver/ContentProvider)。

dagger2.10 中的

@Component.Builder 为我们提供了更好的方法来创建 DaggerAppComponent 的构建器。

Builder 中的

@BindsInstance 会自动创建MyApplication 的实例,因此在AppModule 中我们不需要使用MyApplication 进行实例化。它已经是图中的一个依赖项。

ActivityBindingModule.clas 是我们的。稍后我会告诉你。

您可以在此处阅读有关此部分的更多信息:https://proandroiddev.com/dagger-2-component-builder-1f2b91237856

接下来是 AppModule.class :

@Module
abstract class AppModule

    @Binds
    abstract fun provideContext(application: MyApplication) : Context

    @Module
    companion object 

        @JvmStatic
        @Provides
        fun provideSharedPreferences(context: Context): SharedPreferences =
            context.getSharedPreferences("SharedPreferences", Context.MODE_PRIVATE)
    

@Binds 注释替换了@Provides 注释,它只返回函数参数中的值。如您所见,图中已经有一个 MyApplication 实例,无需在 AppModule 构造函数中注入 MyApplication。

注意:带有@Binds 注释的函数应该是抽象的,如果有带有@Provides 注释的函数,它们应该是static。 您可以在这里找到 Kotlin 中静态功能的解决方案:https://github.com/google/dagger/issues/900

ActivityBindingModule.class:

@Module
abstract class ActivityBindingModule 

    @ContributesAndroidInjector(modules = [MainActivityModule::class])
    internal abstract fun bindMainActivity(): MainActivity

使用 ActivityBindingModule 类,我们只需创建单独的模块,该模块将为我们创建 Android 组件的 子组件

有关 ContributesAndroidInjector 和 Binds 的更多信息,您可以在此处阅读: https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f

MainActivityModule.class:

@Module
abstract class MainActivityModule 

    @Binds
    internal abstract fun provideMainActivity(activity: MainActivity): MainActivity

MyApplication.class:

class MyApplication: DaggerApplication()

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> 
        val appComponent = DaggerAppComponent.builder()
            .application(this)
            .build()
        appComponent.inject(this)
        return appComponent
    

不要忘记在 Mainfest 文件中插入应用程序。

<application
    android:name=".MyApplication"
...
>
    ...
</application>

对于您的 Application 类,您需要实现 DaggerApplication 来实现 HasActivityInjector/HasFragmentInjector/etc 以及调用 AndroidInjection.inject()。

关于这个你可以在这里阅读更多:https://google.github.io/dagger/android.html

MainActivity.class:

class MainActivity : DaggerAppCompatActivity() 

    @Inject
    lateinit var sharedPreferences: SharedPreferences

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("TAAAAG", sharedPreferences.toString())
    

如您所见,MainActivity 现在不知道 SharedPreferences 是如何注入的。实际上在 DaggerAppCompatActivity 中有AndroidInjection.inject(this);。如果你不从这里扩展你的类,那么你需要自己在 onCreate 方法中指定它,否则不会进行注入。

编辑: 您可以从 GitHub 上查看代码:https://github.com/Belka1000867/Dagger2Kotlin

【讨论】:

哇,这很好。我会好好看看这个。我讨厌 Dagger 的一件事是,您不能创建一个执行此注入的基本活动,而只是扩展它以及基本活动中定义的所有注入模块和组件 所以我刚刚查看了您的以下示例并提出了一个问题。我需要为我使用的每个活动创建一个 ActivityModule 吗?然后简单地将该活动添加为组件中的新注入方法似乎更麻烦?有没有办法只使用基础活动或类来进行所有注入? 我做了一个github项目以获得更好的视图。 github.com/Belka1000867/Dagger2Kotlin 在仔细分析您的代码并在我的项目上应用相同的工作流程后,我收到一个 IllegalStateException 说我的绑定没有为 BindingKey(我的视图回调演示者)解决。然后我尝试从模块中删除我在相关绑定上的作用域@Singleton,然后它解释说我无法从无作用域的子组件中引用作用域绑定 如果我理解正确,您在子组件中有一个 @Singleton 范围,对吧?但是单例最好在 Application 模块中创建,这样就不会被破坏。对于您提供的每个组件,范围只能是一个。关于组件有一篇很好的文章frogermcs.github.io/building-userscope-with-dagger2。

以上是关于Dagger 2 不注入 sharedPreference的主要内容,如果未能解决你的问题,请参考以下文章

Dagger 2从浅到深

Dagger 2 注入 Android 应用程序上下文

Dagger 2 构造函数注入等

Android 依赖注入: Dagger 2 实例解说

Dagger2:无法在 WorkManager 中注入依赖项

使用Dagger2做静态注入, 对比Guice.