Dagger 2注入Android应用上下文

57

我正在使用Dagger 2,并使其正常工作,但现在我需要访问Android应用程序上下文

我不清楚如何注入并访问上下文,我尝试按照以下方式实现:

@Module
public class MainActivityModule {    
    private final Context context;
    
    MainActivityModule(Context context) {
        this.context = context;
    }

    @Provides @Singleton
    Context provideContext() {
        return context;
    }
}

然而,这会导致以下异常:

java.lang.RuntimeException: Unable to create application : java.lang.IllegalStateException: mainActivityModule must be set

如果我检查Dagger生成的代码,就会在此处引发此异常:

public Graph build() {  
    if (mainActivityModule == null) {
        throw new IllegalStateException("mainActivityModule must be set");
    }
    return new DaggerGraph(this);
}

我不确定这是否是获取注入上下文的正确方法 - 任何帮助将不胜感激。


我不确定注入应用程序上下文是否有意义。您可以扩展Application类并创建Application的静态实例。您可以将其命名为例如BaseApplication。之后,您可以在扩展的Application类中创建一个get()方法,该方法将返回该实例并同时是Application Context。然后,您可以使用以下结构从项目中的任何位置访问Application Context:BaseApplication.get()。您应该谨慎使用它,并且仅在必要时使用。 - Piotr Wittchen
6个回答

34
@Module
public class MainActivityModule {    
    private final Context context;

    public MainActivityModule (Context context) {
        this.context = context;
    }

    @Provides //scope is not necessary for parameters stored within the module
    public Context context() {
        return context;
    }
}

@Component(modules={MainActivityModule.class})
@Singleton
public interface MainActivityComponent {
    Context context();

    void inject(MainActivity mainActivity);
}

然后

MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
    .mainActivityModule(new MainActivityModule(MainActivity.this))
    .build();

1
@IgorGanapolsky 我不确定为什么我将它与AppContext一起使用。但它基本上是以相同的方式运作的。 - EpicPandaForce
@EpicPandaForce 在一个Activity中,我应该使用DI还是只用'this'?例如,在一个Activity中显示对话框。 - Dr.jacky
对于对话框,我会选择使用“this”。 - EpicPandaForce
2
因为作用域提供程序不需要为其提供相同的实例。它只会存在一次,因为它始终从模块中的字段提供。 - EpicPandaForce
1
能够现场注入 MainActivity @JohanLund - EpicPandaForce
显示剩余4条评论

16

我花了一些时间才找到一个适当的解决方案,所以想分享给其他人,据我所知,这是当前 Dagger 版本(2.22.1)中的首选解决方案。

在下面的示例中,我需要使用ApplicationContext来创建RoomDatabase(在StoreModule中发生)。

如果您发现任何错误或错误,请告诉我,这样我也可以学习:)

组件:

// We only need to scope with @Singleton because in StoreModule we use @Singleton
// you should use the scope you actually need
// read more here https://google.github.io/dagger/api/latest/dagger/Component.html
@Singleton
@Component(modules = { AndroidInjectionModule.class, AppModule.class, StoreModule.class })
public interface AwareAppComponent extends AndroidInjector<App> {

    // This tells Dagger to create a factory which allows passing 
    // in the App (see usage in App implementation below)
    @Component.Factory
    interface Factory extends AndroidInjector.Factory<App> {
    }
}

应用程序模块:

@Module
public abstract class AppModule {
    // This tell Dagger to use App instance when required to inject Application
    // see more details here: https://google.github.io/dagger/api/2.22.1/dagger/Binds.html
    @Binds
    abstract Application application(App app);
}

存储模块:

@Module
public class StoreModule {
    private static final String DB_NAME = "aware_db";

    // App will be injected here by Dagger
    // Dagger knows that App instance will fit here based on the @Binds in the AppModule    
    @Singleton
    @Provides
    public AppDatabase provideAppDatabase(Application awareApp) {
        return Room
                .databaseBuilder(awareApp.getApplicationContext(), AppDatabase.class, DB_NAME)
                .build();
    }
}

应用程序:

public class App extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();

        // Using the generated factory we can pass the App to the create(...) method
        DaggerAwareAppComponent.factory().create(this).inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }
}

1
2020年最有用的 - Akbolat SSS

8

我已阅读此文章,它非常有帮助。

https://medium.com/tompee/android-dependency-injection-using-dagger-2-530aa21961b4

示例代码。

更新:我从AppComponent.kt中删除了这些行,因为它们不是必需的。

fun context(): Context
fun applicationContext(): Application

AppComponent.kt

   @Singleton
    @Component(
        modules = [
            NetworkModule::class,
            AppModule::class
        ]
    )
    interface AppComponent {
        fun inject(viewModel: LoginUserViewModel)
    }

AppModule.kt

@Module
class AppModule(private val application: Application) {

    @Provides
    @Singleton
    fun providesApplication(): Application = application

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

    @Singleton
    @Provides
    fun providesNetworkConnectivityHelper(): NetworkConnectivityHelper{
        return NetworkConnectivityHelper(application.applicationContext)
    }
}

NetworkConnectivityHelper.kt

仅添加了@Inject构造函数以传递上下文

class NetworkConnectivityHelper @Inject constructor(context: Context) {

    private val connectivityManager =
        context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

    @Suppress("DEPRECATION")
    fun isNetworkAvailable(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val nc = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)

            nc != null
                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        }

        val networkInfo = connectivityManager.activeNetworkInfo
        return networkInfo != null && networkInfo.isConnected
    }
}

App类.kt

class App : Application() {

    lateinit var appComponent: AppComponent

    override fun onCreate() {
        super.onCreate()
        this.appComponent = this.initDagger()
    }

    private fun initDagger() = DaggerAppComponent.builder()
        .appModule(AppModule(this))
        .build()
}

最后在我的Activity中注入了我的辅助类

 @Inject lateinit var networkConnectivity: NetworkConnectivityHelper

而且YEI!它对我有效。


1
请查看此问题:https://dev59.com/yLjna4cB1Zd3GeqP8VKY - Malwinder Singh
我刚刚回答了! - Adolfo Chavez

2
@Singleton
@Component(modules = [YourModule::class, ThatOtherModule::class])
interface ApplicationComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance fun applicationContext(applicationContext: Context): Builder
        fun build(): ApplicationComponent
    }
}

class YourApplication : Application() {

    val component: ApplicationComponent by lazy {
        DaggerApplicationComponent.builder()
            .applicationContext(applicationContext)
            .build()
    }
}
  • 使用@BindInstance声明一个抽象函数,提供上下文依赖。例如:@BindsInstance fun applicationContext(applicationContext: Context): Builder
  • 使用.applicationContext(applicationContext)设置上下文。

2

1
也许我们可以像下面这样注入上下文:
应用程序组件
@Component(
    modules = [
        (ApplicationModule::class),
        (AndroidSupportInjectionModule::class),
        (UiBindingModule::class)
    ]
)
interface ApplicationComponent : AndroidInjector<AndroidApplication> {

    override fun inject(application: AndroidApplication)

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: AndroidApplication): Builder

        @BindsInstance
        fun context(context: Context): Builder

        fun build(): ApplicationComponent
    }
}

定制应用程序扩展Dagger应用程序。
class AndroidApplication : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerApplicationComponent.builder().application(this).context(this).build()
    }
}

示例应用程序模块
@Module
abstract class ApplicationModule {

    /**
     * Binds a background thread executor, which executes threads from a thread pool
     * @param jobExecutor
     * @return
     */
    @Binds
    internal abstract fun provideThreadExecutor(jobExecutor: JobExecutor): ThreadExecutor

    /**
     * Binds main ui looper thread
     * @param uiThread
     * @return
     */
    @Binds
    internal abstract fun providePostExecutionThread(uiThread: UIThread): PostExecutionThread

}

示例UI绑定模块。
@Module
abstract class UiBindingModule {

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

    @ContributesAndroidInjector(modules = [(MapFragmentModule::class)])
    internal abstract fun mapFragment(): MapFragment

}

1
如果使用Android注入,这实际上是最佳解决方案。 - Nicolás Arias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接