如何使用 Dagger 2 提供上下文?

问题描述 投票:0回答:4

我正在创建使用 Dagger 注入一些依赖项的应用程序。

我创建了一些模块类,但其中之一需要上下文。问题是我不知道如何以正确的方式提供上下文。我尝试了很多解决方案,但每次我都会遇到异常:

@Component.Builder 缺少所需模块或组件的设置器:[com.mamak.geobaza.di.ContextModule]

我应该怎样做才能以正确的方式提供上下文?

ApiModule.kt

package com.mamak.geobaza.di

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.mamak.geobaza.utils.AppConstans.BASE_URL
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton

@Module(includes = [
    RxJavaModule::class,
    OkHttpModule::class
])
class ApiModule {
    @Provides
    @Singleton
    fun gson(): Gson {
        return GsonBuilder().create()
    }

    @Provides
    @Singleton
    fun gsonConverterFactory(gson: Gson): GsonConverterFactory {
        return GsonConverterFactory.create(gson)
    }

    @Provides
    @Singleton
    fun retrofit(
        okHttpClient: OkHttpClient,
        gsonConverterFactory: GsonConverterFactory,
        rxJava2CallAdapterFactory: RxJava2CallAdapterFactory
    ): Retrofit {
        return Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(BASE_URL)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(rxJava2CallAdapterFactory)
            .build()
    }
}

InterfaceModule.kt

package com.mamak.geobaza.di

import android.content.Context
import com.mamak.geobaza.ui.`interface`.ProjectListItemInterfaceImpl
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module(includes = [
    ContextModule::class
])
class InterfaceModule {
    @Provides
    @Singleton
    fun projectListItemInterface(context: Context): ProjectListItemInterfaceImpl {
        return ProjectListItemInterfaceImpl(context)
    }
}

OkHttpModule.kt

package com.mamak.geobaza.di

import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
class OkHttpModule {
    @Provides
    @Singleton
    fun okHttpLoggingInterceptor(): HttpLoggingInterceptor {
        return HttpLoggingInterceptor()
            .setLevel(HttpLoggingInterceptor.Level.BODY)
    }

    @Provides
    @Singleton
    fun okHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(httpLoggingInterceptor)
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build()
    }
}

ProjectApiModule.kt

package com.mamak.geobaza.di

import com.mamak.geobaza.network.api.ProjectApiService
import dagger.Module
import retrofit2.Retrofit

@Module(includes = [
    ApiModule::class
])
class ProjectApiModule {
    fun projectApiService(retrofit: Retrofit): ProjectApiService {
        return retrofit.create(ProjectApiService::class.java)
    }
}

RxJavaModule.kt

package com.mamak.geobaza.di

import dagger.Module
import dagger.Provides
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import javax.inject.Singleton

@Module
class RxJavaModule {
    @Provides
    @Singleton
    fun rxJavaCallAdapterFactory(): RxJava2CallAdapterFactory {
        return RxJava2CallAdapterFactory.create()
    }
}

ViewModelKey.kt

package com.mamak.geobaza.di

import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

ViewModelModule.kt

package com.mamak.geobaza.di

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.mamak.geobaza.factory.ViewModelFactory
import com.mamak.geobaza.ui.viewmodel.ProjectListViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
internal abstract class ViewModelModule {
    @Binds
    internal abstract fun viewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(ProjectListViewModel::class)
    protected abstract fun projectListViewModel(projectListViewModel: ProjectListViewModel): ViewModel

}

ContextModule.kt

package com.mamak.geobaza.di

import android.content.Context
import dagger.Module
import dagger.Provides

@Module
class ContextModule constructor(val context: Context) {
    @Provides
    fun context(): Context {
        return context
    }
}

AppComponent.kt

package com.mamak.geobaza.di

import android.app.Application
import dagger.BindsInstance
import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton

@Singleton
@Component(modules = [
    ApiModule::class,
    ViewModelModule::class,
    AndroidSupportInjectionModule::class,
    InterfaceModule::class,
    ContextModule::class
])
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        @BindsInstance
        fun apiModule(apiModule: ApiModule): Builder

        @BindsInstance
        fun interfaceModule(interfaceModule: InterfaceModule): Builder

        @BindsInstance
        fun contextModule(contextModule: ContextModule): Builder

        fun build(): AppComponent
    }

    fun inject(appController: AppController)
}

AppController.kt

package com.mamak.geobaza.di

import android.app.Application

import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import javax.inject.Inject

class AppController : Application(), HasAndroidInjector {
    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>

    override fun androidInjector(): AndroidInjector<Any> {
        return dispatchingAndroidInjector
    }

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.Builder()
            .application(this)
            .apiModule(ApiModule())
            .interfaceModule(InterfaceModule())
            .contextModule(ContextModule(this))
            .build()
            .inject(this)
    }
}
android kotlin dagger
4个回答
3
投票

@Component.Builder 你们都很擅长使用,但它还可以进一步优化。

更改如下:

第 1 步:在上下文模块中使用

@Binds
提供
Context

package com.mamak.geobaza.di

import android.content.Context
import dagger.Module
import dagger.Provides

@Module
abstract class ContextModule {  // to allow abstract method make module abstract

  //@Binds works on an abstract method

    @Singleton
    @Binds   // @Binds, binds the Application instance to Context
    abstract fun context(appInstance:Application): Context //just return the super-type you need 

}

第2步:删除

AppComponent

中的冗余代码
package com.mamak.geobaza.di

import android.app.Application
import dagger.BindsInstance
import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton

@Singleton
@Component(modules = [
    ApiModule::class,
    ViewModelModule::class,
    AndroidSupportInjectionModule::class,
    InterfaceModule::class,
    ContextModule::class
])
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        @BindsInstance
        fun apiModule(apiModule: ApiModule): Builder

        @BindsInstance
        fun interfaceModule(interfaceModule: InterfaceModule): Builder

       // @BindsInstance   //this two  commented lines can be removed 
       // fun contextModule(contextModule: ContextModule): Builder

       // why? because dagger already knows how to provide Context Module 

        fun build(): AppComponent
    }

    fun inject(appController: AppController)
}

第 3 步:修改组件生成器以利用

@Component.Builder
@Binds

package com.mamak.geobaza.di

import android.app.Application

import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import javax.inject.Inject

class AppController : Application(), HasAndroidInjector {
    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>

    override fun androidInjector(): AndroidInjector<Any> {
        return dispatchingAndroidInjector
    }

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.Builder()
            .application(this)
            .apiModule(ApiModule())
            .interfaceModule(InterfaceModule())
           // .contextModule(ContextModule(this)) //this line can be removed 
            .build()
            .inject(this)
    }
}

3
投票

通过使用

component factory
方法,您可以避免手动实例化模块。请检查下面的示例。

@Component(
    modules = [ApplicationModule::class]
)
@Singleton
interface ApplicationComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): ApplicationComponent
    }
}

在您的应用程序类中,初始化对象图,如下所示

class MyApplication : Application() {
    lateinit var applicationComponent: ApplicationComponent

    override fun onCreate() {
        super.onCreate()

        applicationComponent = DaggerApplicationComponent.factory().create(this)
    }
}

现在

context
将可以在您的对象图中被注入到任何地方。


0
投票

通过添加以下代码来添加新模块

@Module
class ApplicationModule(var app: App) {
    @Provides
    @Singleton
    fun provideApp(): App = app

    @Provides
    @Singleton
    fun provideContext(): Context = app.applicationContext
}

然后将此模块添加到您的组件接口中

@Component(modules = arrayOf(ApplicationModule::class))

最后在应用程序(

class App : android.app.Application()
)类中创建组件

val component by lazy {
    DaggerApplicationComponent.builder()
            .applicationModule(ApplicationModule(this))
            .build()
}

0
投票

不再需要以前答案中的所有代码,只需在需要上下文的地方使用

@ApplicationContext

@Provides
fun provideFirebaseAnalytics(@ApplicationContext context: Context) =
    FirebaseAnalytics.getInstance(context)
© www.soinside.com 2019 - 2024. All rights reserved.