Dagger 2,提供Module中的应用程序上下文

8

我在Android开发和依赖注入方面都很新。我正在使用Kotlin进行个人项目实验,尝试使用Dagger 2。我已经成功为一个工具类设置了依赖注入,但是当我需要有一个上下文来注入需要上下文的类(例如SharedPref管理类)时,我失败了。这是我的代码以及我收到的错误(NPE)。谢谢。

我的模块类

package com.android.pine

import android.content.Context
import com.android.pine.utils.SharedPreferencesManager
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module
class AppModule {

    @Provides
    @Singleton
    fun context(pineApplication: PineApplication): Context = pineApplication.applicationContext

    @Provides
    @Singleton
    fun provideSharedPrefManager(context: Context): SharedPreferencesManager = SharedPreferencesManager(context)
}

我的组件类:

package com.android.pine

import com.android.pine.home.HomePresenter
import com.android.pine.home.categories.CategoryAdapter
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    fun inject(categoryAdapter: CategoryAdapter)
    fun inject(homePresenter: HomePresenter)
}

编辑:以下是新增信息,我如何调用sharedPreferencesManager的注入:

class HomePresenter : BasePresenter<HomeView>() {

    @Inject
    lateinit var sharedPreferencesManager: SharedPreferencesManager
.
.
.

在我的HomePresenter类中,在onAttached方法中重写以下代码:
 DaggerAppComponent.create().inject(this)

我的pineApplication类和SharedPrefManager类如下:

class PineApplication @Inject constructor(): Application()

SharedPref:

class SharedPreferencesManager @Inject constructor(context: Context) {
.
.
.

崩溃,无法获取pineApplication.getContext() (编辑,添加完整的堆栈跟踪)

     06-02 11:57:01.028 14840-14840/com.android.pine.debug E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.pine.debug, PID: 14840
java.lang.RuntimeException: Unable to resume activity {com.android.pine.debug/com.android.pine.home.HomeActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3429)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3469)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732)
    at android.app.ActivityThread.-wrap12(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6119)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
    at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:106)
    at com.android.pine.AppModule.context(AppModule.kt:12)
    at com.android.pine.AppModule_ContextFactory.proxyContext(AppModule_ContextFactory.java:34)
    at com.android.pine.DaggerAppComponent.getContext(DaggerAppComponent.java:29)
    at com.android.pine.DaggerAppComponent.getSharedPreferencesManager(DaggerAppComponent.java:34)
    at com.android.pine.DaggerAppComponent.injectHomePresenter(DaggerAppComponent.java:59)
    at com.android.pine.DaggerAppComponent.inject(DaggerAppComponent.java:49)
    at com.android.pine.home.HomePresenter.onAttached(HomePresenter.kt:31)
    at com.android.pine.home.HomePresenter.onAttached(HomePresenter.kt:10)
    at com.android.pine.core.BaseActivity.onResume(BaseActivity.kt:34)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269)
    at android.app.Activity.performResume(Activity.java:6783)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3406)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3469) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732) 
    at android.app.ActivityThread.-wrap12(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6119) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776

请包含完整的堆栈跟踪和调用/注入代码的位置/方式,这似乎相当奇怪。 - David Medenjak
编辑了帖子,添加了更多信息,对问题不完整表示歉意。 - paxcow
你的代码中存在几个错误,这些错误基于对Android和Dagger的误解。这没关系,我们都会遇到这种情况。我看到的一个问题是你正在注入一个presenter,但由于你完全控制该类,所以应该使用构造函数注入。请查看此博客,其中详细说明了如何使用Dagger设置Android项目:https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj - AutonomousApps
4个回答

8
这就是做法。在组件中使用@BindsInstance将应用程序注入到所有模块中。在你的情况下,只需注入到AppModule中即可。
@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    @Component.Builder
    interface Builder() {
        fun build(): AppComponent

        @BindsInstance
        fun application(application: Application): Builder
    }
}

请在您的APP模块中删除“提供应用程序”功能的代码,并确保将应用程序上下文传递给create sharedPreferences。

@Module
class AppModule {


@Provides
@Singleton
fun provideSharedPrefManager(context: Application): SharedPreferencesManager = 
    SharedPreferencesManager(context)
}

在你的 applicationClass 的 onCreate 方法中:
DaggerAppComponent.builder().application(this).build()

可选:如果您想将某些内容注入到您的applicationClass中,请按照以下步骤操作

 DaggerAppComponent.builder().application(this).build().inject(this)

4
为什么我在任何帖子中都找不到关于这个简单且可行示例的答案。我发誓,如果找不到这个,我就要跳楼了。请帮我翻译成中文。 - keesjanbertus
如果我理解正确的话,我需要在MainActivity中获取上下文并在那里构建AppComponent。但是,接下来我需要注入一些东西,例如一个fragment。我在那里没有上下文,那么我该怎么做呢? - Oleg Yablokov

4
您不能使用class PineApplication @Inject constructor(): Application()来创建PineApplication,因为它是一个框架类,必须由Android Framework创建。
这样做Dagger会创建PineApplication,但是applicationContext将返回null,因为它从未被系统初始化过。
不要对框架类使用构造函数注入,也不要自己创建。使用@Bindsintance将对象添加到具有其构建器的组件中,或使用模块提供它。

1
请问您能否给出一个在这种情况下如何使用@BindInstance的示例? - paxcow
@tipi 你可以查看JavaDoc,或者参考用户指南中的示例。 - David Medenjak

3
您可以尝试像这样修改您的应用程序模块。
@Module
class ApplicationModule(private val application: Application) {

    @Provides
    @Singleton
    fun provideContext(): Context {
        return application.applicationContext
    }

    @Provides
    @Singleton
    fun provideSharedPreferences(context: Context): SharedPreferences {
        return context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE)
    }
}

然后,您可以从您的Application类构建这样的dagger组件。
val appComponent = DaggerAppComponent.builder()
                .applicationModule(ApplicationModule(this))
                .build()

像这样在Presenter中注入值。

application.appComponent.inject(this)

0
为了提供应用程序上下文,您可以创建一个类,例如ComponentsManager,其中包含:
public class ComponentsManager {

    private Context context;
    private AppComponent appComponent;

    public ComponentsManager(Context context) {
        this.context = context.getApplicationContext();
    }

    public AppComponent getAppComponent(){
        if (appComponent == null){
            appComponent = DaggerAppComponent.builder()
                    .appModule(new AppModule(context))
                    .build();
        }
        return appComponent;
    }
}

在你的应用程序类中进行初始化,像这样:ComponentsManager
public class YourApp extends Application {

    private static ComponentsManager componentsManager;

    @Override
    public void onCreate() {
        super.onCreate();
        initComponentsManager();
        initAppComponent();
    }

    public static ComponentsManager getComponentsManager(){
        return componentsManager;
    }

    private void initComponentsManager(){
        componentsManager = new ComponentsManager(this);
    }

    private void initAppComponent(){
        componentsManager.getAppComponent();
    }
}

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