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的主要内容,如果未能解决你的问题,请参考以下文章