Передача контекста приложения в действии в Dagger 2 (NullPointerException)

Я пытаюсь передать Activity из контекста приложения в Dagger 2. Любая помощь приветствуется !! В конце я предоставил свою консоль Logcat.

Проект Грегори Кика дает мне ту же проблему, что и показано ниже в последнем разделе об ошибках. https://github.com/gk5885/dagger-android-sample

Я также пытался улучшить на основе этой проблемы: https://github.com/google/dagger/issues/832

Ничего не получилось! Я знаю, что контекст приложения на самом деле не нужен в Activity, потому что в Activity есть контекст Activity. Но просто интересно узнать, как передать контекст приложения в любые классы (например, Activity)?

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    buildToolsVersion "27.0.3"

    testBuildType "staging"

    testOptions {
        reportDir "$rootDir/test-reports"
        resultsDir "$rootDir/test-results"
        unitTests {
            returnDefaultValues true
            all {
                // Sets JVM argument(s) for the test JVM(s).
                jvmArgs '-XX:MaxPermSize=256m'

                // You can also check the task name to apply options to only the tests you specify.
                if (it.name == 'testDebugUnitTest') {
                    systemProperty 'debug', 'true'
                }
            }
        }
    }
    defaultConfig {
        applicationId "com.nexuslab.forensics.grr.nanny"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        testHandleProfiling true
        testFunctionalTest true
    }
    signingConfigs {
        debugKey {
            keyAlias 'android'
            keyPassword 'android'
            storeFile file('keys/platform.jks')
            storePassword 'android'
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debugKey
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        staging {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    compileOnly 'org.projectlombok:lombok:1.16.20'
    annotationProcessor 'org.projectlombok:lombok:1.16.20'
    //java vm based test
    testImplementation 'org.hamcrest:hamcrest-core:1.3'
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.powermock:powermock-api-mockito:1.6.5'
    testImplementation 'org.powermock:powermock-module-junit4-rule-agent:1.6.5'
    testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.5'
    testImplementation 'org.powermock:powermock-module-junit4:1.6.5'
    testImplementation 'org.mockito:mockito-core:2.15.0'
    //instrumentation test
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    androidTestImplementation 'com.android.support:support-annotations:27.1.0'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test:rules:1.0.1'
    androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
    androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
    androidTestImplementation 'junit:junit:4.12'
    //dagger 2
    implementation 'com.google.dagger:dagger:2.14.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
    //to enable DaggerActivity, DaggerBroadcastReceiver, DaggerFragment etc classes
    implementation 'com.google.dagger:dagger-android:2.14.1'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
    //support libraries with dagger 2
    implementation 'com.google.dagger:dagger-android-support:2.14.1'
    implementation 'com.android.support:support-v4:27.1.0'
}

Класс приложения (NannyApplication.java)

package com.nexuslab.forensics.grr.nanny;

import android.app.Application;

import com.nexuslab.forensics.grr.nanny.di.component.DaggerNannyApplicationComponent;
/**
 * Created by gaute on 3/25/18.
 */

public class NannyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerNannyApplicationComponent.builder().create(this);
    }
}

MainActivity.java (здесь я хочу ввести контекст из NannyApplication.java)

package com.nexuslab.forensics.grr.nanny;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import javax.inject.Inject;

/**
 * @author gaute
 */
public class MainActivity extends AppCompatActivity {

    @Inject
    NannyApplication nannyApplication;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Utils.schedule(nannyApplication, HeartbeatService.class, Constants.HEARTBEAT_CHECK_INTERVAL);
        finishAndRemoveTask();
    }

}

Компонент приложения Dagger (NannyApplicationComponent.java)

package com.nexuslab.forensics.grr.nanny.di.component;


import com.nexuslab.forensics.grr.nanny.NannyApplication;
import com.nexuslab.forensics.grr.nanny.di.module.NannyApplicationModule;

import javax.inject.Singleton;

import dagger.Component;
import dagger.android.AndroidInjector;

/**
 * Created by gaute on 3/25/18.
 */

@Singleton
@Component(modules = NannyApplicationModule.class)
public interface NannyApplicationComponent extends AndroidInjector<NannyApplication> {

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<NannyApplication> {
    }
}

Модуль приложения (NannyApplicationModule.java)

package com.nexuslab.forensics.grr.nanny.di.module;

import com.nexuslab.forensics.grr.nanny.NannyApplication;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;
import dagger.android.AndroidInjectionModule;

@Module(includes = AndroidInjectionModule.class)
public class NannyApplicationModule {

    @Provides
    @Singleton
    NannyApplication getNannyApplication(NannyApplication nannyApplication) {
        return nannyApplication;
    }

}

Ошибка, я получил

03-27 19:08:37.107 10619-10619/com.nexuslab.forensics.grr.nanny E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.nexuslab.forensics.grr.nanny, PID: 10619
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.nexuslab.forensics.grr.nanny/com.nexuslab.forensics.grr.nanny.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] com.nexuslab.forensics.grr.nanny.NannyApplication.databaseList()' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2946)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3046)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1688)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6809)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] com.nexuslab.forensics.grr.nanny.NannyApplication.databaseList()' on a null object reference
        at com.nexuslab.forensics.grr.nanny.MainActivity.onStart(MainActivity.java:26)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1412)
        at android.app.Activity.performStart(Activity.java:7015)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2909)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3046) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1688) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6809) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

Я не могу использовать какие-либо виды внедрения конструктора в Activity, как показано ниже.

 private NannyApplication nannyApplication;

    @Inject
    public MainActivity() {
        this.nannyApplication = nannyApplication;
    }

Вот почему я использовал поданную инъекцию:

@Inject NannyApplication nannyApplication; //Problem here

Я вижу пустой onCreate и не звоню на activityComponent.inject(activity). Куда вы вкладываете свою активность? @Inject не будет вводить какие-либо поля без вызываемого компонента.

David Medenjak 28.03.2018 08:43

@DavidMedenjak, спасибо! Для меня немного сложно понять фреймворк, созданный Dagger 2. Я решил проблему. Я опубликую обновленный ответ со всеми подробностями объяснения, чтобы он действительно помог всем, кто склоняется к Dagger 2.

Uddhav Gautam 28.03.2018 22:51

Я только что создал образец проекта для этой проблемы, чтобы он помог другим пользователям, которые ждут моего ответа. github.com/uddhavgautam/Dagger2ApplicationContextToActivity

Uddhav Gautam 28.03.2018 23:37
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
778
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

внесите некоторые изменения в класс приложения, как показано ниже ...

public class NannyApplication extends Application {
private DaggerNannyApplicationComponent daggerNannyApplicationComponent;

@Override
public void onCreate() {
    super.onCreate();
    daggerNannyApplicationComponent.builder().create().build();
}

public DaggerNannyApplicationComponent getComponent() {
    return daggerNannyApplicationComponent;
}

}

daggerNannyApplicationComponent.builder().create().build(); неверен, поскольку builder() - статический метод. Во-вторых, зачем мне здесь метод getComponent(), ведь я нигде не использую daggerNannyApplicationComponent.
Uddhav Gautam 28.03.2018 08:33

Если вы хотите, я предлагаю одну демонстрацию для базы данных комнат с использованием dagger 2.

Android Team 28.03.2018 09:05
Ответ принят как подходящий

Это рабочее решение.

Правило: для любого служебного компонента T мы должны предоставить экземпляр T как seedInstance в класс AndroidInjector.Builder<T>. Это означает, что если вы выполняете AndroidInjector.inject(this) внутри обратного вызова жизненного цикла MainActivity, вы инициализировали seedInstance = mainActivity внутри класса AndroidInjector.Builder<MainActivity>.

Этот AndroidInjector.Builder<MainActivity> вы увидите внутри внутреннего класса DaggerNannyApplicationComponent's Builder, потому что в NannyApplicationComponent вы напишете

@Component.Builder /* Simply tells this Builder is DaggerNannyApplicationComponent’s inner Builder class */
    abstract class Builder extends AndroidInjector.Builder<NannyApplication> {
    } 

Пример проекта в Github: https://github.com/uddhavgautam/Dagger2ApplicationContextToActivity

NannyApplicationComponent.java

@Singleton
@Component(modules = {
        AndroidSupportInjectionModule.class /* it makes Dagger generates DaggerNannyApplicationComponent */,
        ApplicationBindingModule.class /* it generats AndroidInjector.Builder<MainActivity>, which
        is used to inject requested dependencies by MainActivity */
})
public interface NannyApplicationComponent extends AndroidInjector<NannyApplication> {

    @Component.Builder /* Simply tells this Builder is DaggerNannyApplicationComponent’s inner Builder class */
    abstract class Builder extends AndroidInjector.Builder<NannyApplication> {
    }
}

ApplicationBindingModule.java

 @Module
    public interface ApplicationBindingModule {

/* These two lines, actually, makes Dagger generates ApplicationBindingModule_MainActivity class */
        @ContributesAndroidInjector
        MainActivity mainActivity();
    }

НяняApplicationModule.java

@Module
public class NannyApplicationModule {
}

MainActivity.java

//MainActivity is a consumer because it has requested nannyApplication using @Inject annotation
public class MainActivity extends DaggerAppCompatActivity {

    @Inject
    NannyApplication nannyApplication /* You got the ApplicationContext */;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // AndroidInjection.inject(this); 
/* if Consumers want MainActivity instance then uncomment AndroidInjection.inject(this) line. Doing this makes MainActivity Consumer as well as Service component. Because it consumed nannyApplication but it has also provided (serviced) it's instance to the external world */
    }

    @Override
    protected void onStart() {
        super.onStart();
        //check nannyApplication
        Log.i("Cls-loader: ", nannyApplication.getClassLoader() + "");
    }
}

NannyApplication.java

public class NannyApplication extends DaggerApplication {

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

    /**
     * applicationInjector() gets called inside onCreate()
     */
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerNannyApplicationComponent
                .builder()
                .create(this);
    }
}

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