Dagger 2, Providing Application Context in Module

情到浓时终转凉″ 提交于 2020-04-06 09:05:34

问题


Im pretty new in Android development and newer in DI. I am using Kotlin on a personal project where I am experimenting with Dagger 2. I managed to set it up for a util class, but where i need to have a context to use it to inject a class that requires a context (a sharedpref manager class), I failed. Here is my code, and here is the error ( NPE ) i am getting. Thank you in advance.

my module class

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)
}

my component class:

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)
}

edit: added the info below, How I call the inject of sharedPreferencesManager:

class HomePresenter : BasePresenter<HomeView>() {

    @Inject
    lateinit var sharedPreferencesManager: SharedPreferencesManager
.
.
.

Also in my HomePresenter class, in onAttached method override:

 DaggerAppComponent.create().inject(this)

My pineApplication class and SharedPrefManager class looks like this:

class PineApplication @Inject constructor(): Application()

SharedPref:

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

Crash, can not get the pineApplication.getContext() (edited, added the full stack trace)

     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) 

回答1:


You can't use class PineApplication @Inject constructor(): Application() to create PineApplication. It's a framework class and has to be created by the Android Framework.

Doing so Dagger will create PineApplication, but applicationContext will return null as it has never been initialized (by the system).

Don't use constructor injection for framework classes and don't create the yourself. Use @Bindsintance to add the object to the component with its builder, or use a module to provide it.




回答2:


This is how It is done. Using @BindsInstance in your component will inject application to all of your modules.In your case just to AppModule

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    @Component.Builder
    interface Builder() {
        fun build(): AppComponent

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

** Delete code to "Provides application" function in your APP module and make sure you pass application context to create sharedPreferences.

@Module
class AppModule {


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

and now in your onCreate of applicationClass

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

Optional: if you want to inject something into your applicationClass then do the following

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



回答3:


For provide application context, you can create class, like ComponentsManager with:

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;
    }
}

And in your application class init this ComponentsManager like this:

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();
    }
}



回答4:


You can try modifying your App module like this.

@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)
    }
}

Then you can build the dagger component like this from your Application class.

val appComponent = DaggerAppComponent.builder()
                .applicationModule(ApplicationModule(this))
                .build()

Inject values in presenter like this.

application.appComponent.inject(this)


来源:https://stackoverflow.com/questions/50655722/dagger-2-providing-application-context-in-module

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!