Dagger 2, предоставление контекста приложения в модуле

Я новичок в разработке Android и новичок в DI. Я использую Kotlin в личном проекте, где я экспериментирую с Dagger 2. Мне удалось настроить его для класса util, но там, где мне нужен контекст, чтобы использовать его для внедрения класса, требующего контекста (менеджер 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 02.06.2018 12:05

отредактировал пост, добавил дополнительную информацию, извиняюсь за неполноту в вопросе

paxcow 02.06.2018 12:13

В вашем коде есть несколько ошибок, вызванных неправильным пониманием как Android, так и Dagger. Это нормально, мы все это переживаем. Одна проблема, которую я вижу, заключается в том, что вы вводите презентатора, но, поскольку вы полностью контролируете этот класс, вам следует просто использовать внедрение конструктора. Взгляните на этот блог, в котором подробно объясняется, как настроить проект Android с помощью Dagger dev.to/autonomousapps/….

AutonomousApps 02.06.2018 18:09
4
3
6 586
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы не можете использовать class PineApplication @Inject constructor(): Application() для создания PineApplication. Это класс фреймворка, который должен быть создан Android Framework.

При этом Dagger создаст PineApplication, но applicationContext вернет null, поскольку он никогда не был инициализирован (системой).

Не используйте внедрение конструктора для классов фреймворка и не создавайте сами. Используйте @Bindsintance, чтобы добавить объект к компоненту с его построителем, или используйте модуль, чтобы предоставить его.

не могли бы вы привести пример использования @BindInstance в этом случае?

paxcow 02.06.2018 13:31

@tipi Вы можете проверьте javadoc или посмотреть руководство пользователя в качестве примера

David Medenjak 02.06.2018 18:15

Для предоставления контекста приложения вы можете создать класс, например 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();
    }
}

Вы можете попробовать изменить свой модуль приложения следующим образом.

@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.

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

Вставить значения в презентатор следующим образом.

application.appComponent.inject(this)

Вот как это делается. Использование @BindsInstance в вашем компоненте приведет к внедрению приложения во все ваши модули. В вашем случае только в AppModule

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

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

** Удалите код для функции «Предоставляет приложение» в вашем модуле APP и убедитесь, что вы передаете контекст приложения для создания sharedPreferences.

@Module
class AppModule {


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

и теперь в вашем onCreate applicationClass

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

Необязательно: если вы хотите что-то внедрить в свой applicationClass, выполните следующие действия.

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

ПОЧЕМУ я не смог найти ни одного ответа ни на один пост с этим простым и РАБОЧИМ примером. Клянусь богом, я бы выпрыгнул из окна, если бы не нашел этого

keesjanbertus 05.11.2019 20:45

Если я правильно понимаю, я беру контекст в MainActivity и создаю там AppComponent. Но тогда мне нужно что-то ввести, например фрагмент. У меня нет этого контекста, как мне это сделать?

Oleg Yablokov 31.07.2020 11:43

Другие вопросы по теме