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

Posted

技术标签:

【中文标题】Dagger 2 注入 Android 应用程序上下文【英文标题】:Dagger 2 injecting Android Application Context 【发布时间】:2015-08-21 22:29:23 【问题描述】:

我正在使用 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:无法创建应用程序:java.lang.IllegalStateException:必须设置 mainActivityModule

如果我检查 Dagger 生成的代码,这里会引发此异常:

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

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

【问题讨论】:

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

未正确构建应用程序组件,需要传入应用程序。这个 Dagger 2 示例完美地展示了如何做到这一点:https://github.com/google/dagger/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple

更新: 工作链接:https://github.com/yongjhih/dagger2-sample/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple

【讨论】:

【参考方案2】:
@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();

【讨论】:

你能解释一下你在哪里替换 MainActivityModule 吗? @IgorGanapolsky 我不知道为什么我会使用 AppContext。不过,它的工作方式基本相同。 @EpicPandaForce 在活动中,我应该使用 DI 还是只使用“this”?例如在 Activity 中显示 Dialog 因为它不需要作用域提供程序来提供相同的实例。它只会存在一次,因为它总是从模块中的字段提供 现场注入MainActivity@JohanLund的能力【参考方案3】:

我花了一段时间才找到合适的解决方案,因此认为这可能会为其他人节省一些时间,据我所知,这是当前 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;
    

【讨论】:

2020年最有用【参考方案4】:

我读过这篇文章,很有帮助。

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构造函数来传递Context

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
    

应用类.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()

最后在我的活动中我注入了我的助手

 @Inject lateinit var networkConnectivity: NetworkConnectivityHelper

还有耶!它对我有用。

【讨论】:

请看这个问题:***.com/questions/59509103/… 我刚刚回答了!【参考方案5】:

也许我们可以注入如下所示的上下文:

应用程序组件

@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
    

自定义应用扩展匕首应用

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 BindingModule 示例

@Module
abstract class UiBindingModule 

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

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


【讨论】:

如果使用Android注入,这其实是最好的解决方案

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

Android 仪器测试中的 Dagger 2 注入

Android单排上王者系列之Dagger2注入原理解析

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

Android依赖注入Dagger的使用和源码解析(上篇)

应该将演示者(mvP)注入(dagger2)到android中的视图吗?

Android--Dagger2入门