Reputation: 721
I am struggling with my first Dagger2 project.
I want to inject a Context into a singleton class. Here is a minimal example (I have removed package names, imports and have not included layouts, values or manifest files). This code ideally should pick up the string <string name="app_name">My Application</string>
.
MainApplication.java:
public class MainApplication extends Application {
private MainComponent mComponent;
@Override
public void onCreate() {
super.onCreate();
mComponent = DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build();
getComponent().inject(this);
}
public MainComponent getComponent() {
return mComponent;
}
}
MainModule.java:
@Module
public class MainModule {
private final MainApplication mApplication;
public MainModule(MainApplication application) {
this.mApplication = application;
}
@Provides
@Singleton
public Context getApplicationContext() {
return mApplication.getApplicationContext();
}
}
MainComponent.java:
@Singleton
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainApplication application);
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvAppName = findViewById(R.id.main_app_name);
tvAppName.setText(MainHelper.getInstance().getApplicationName());
}
}
MainHelper.java:
public class MainHelper {
private static final MainHelper INSTANCE = new MainHelper();
@Inject
Context mContext;
public static MainHelper getInstance() {
return INSTANCE;
}
public String getApplicationName() {
return mContext.getString(R.string.app_name);
}
}
What actually happens is that I get this exception upon app launch itself:
2019-11-14 18:46:47.561 9116-9116/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 9116
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapplication/com.example.myapplication.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getString(int)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2830)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2909)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1606)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6592)
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:769)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getString(int)' on a null object reference
at com.example.myapplication.MainHelper.getApplicationName(MainHelper.java:21)
at com.example.myapplication.MainActivity.onCreate(MainActivity.java:16)
at android.app.Activity.performCreate(Activity.java:6984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1235)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2783)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2909)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1606)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6592)
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:769)
Basically, the mContext
object in MainHelper
is null which to me means that the injection didn't work at all. What is going wrong? This code seems to adhere to pretty much all the tutorials out there.
Upvotes: 0
Views: 1069
Reputation: 2009
You're not injecting the Context
to your MainHelper
. I suggest you refactor your MainHelper
class to obtain the Context via Constructor injection (you should do this for all classes you own):
@Singleton
public class MainHelper {
private final Context context;
@Inject
public MainHelper(Context context) {
this.context = context;
}
}
The @Inject
annotation on the constructor should be enough so that Dagger knows to instantiate/create MainHelper
for you. And then in order for this MainHelper
class to be injected to your MainActivity
, you first need to tell Dagger that you need to inject dependencies to your MainActivity
. You already did something similar for your MainApplication
class:
@Singleton
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainApplication application);
void inject(MainActivity activity);
}
Then on your MainActivity
, you inject MainHelper
by calling the inject
method that Dagger generates for you:
public class MainActivity extends AppCompatActivity {
@Inject
MainHelper helper;
@Override
protected void onCreate(Bundle savedInstanceState) {
((CustomApplication) getApplication()).getComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvAppName = findViewById(R.id.main_app_name);
tvAppName.setText(helper.getApplicationName());
}
}
Upvotes: 1