Reputation: 21237
I've been working to understand and set up Dagger to handle dependency injections for my Android project. My single (no pun intended) objective is to create singleton objects that I can access across my application. I have successfully set up the objects in the initial activity. Where I am stuck is in accessing those objects from other classes. Here is my setup thus far:
Initial App Activity
public class SplashScreenActivity extends AppCompatActivity {
@Inject SessionKeyExchangerService exchangerService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
AppComponent component = DaggerAppComponent.builder().appModule(new AppModule()).build();
// establish the session id as a singleton object
exchangerService = component.provideSessionKeyExchangerService();
// test whether I can access the singleton from another class
exchangerService.sendEncryptedKeyToServer();
}
Component Class
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
SessionKeyExchangerService provideSessionKeyExchangerService();
AESCipherService provideCipherService();
}
Module Class
@Module
public class AppModule {
@Provides @Singleton
AESCipherService provideCipherService() {
return new AESCipherService();
}
@Provides @Singleton
SessionKeyExchangerService provideSessionKeyExchangerService(AESCipherService service) {
return new SessionKeyExchangerService(service);
}
}
AESCipherService
public class AESCipherService {
private Long sessionId;
public AESCipherService() {
sessionId = makeSessionId();
Log.d(Constants.TAG, "Session ID: " + Long.toString(sessionId));
}
private Long makeSessionId() {
// this generates a random unsigned integer in the space {0, 2^32-1)
Random random = new Random();
return random.nextLong() & 0xffffffffL;
}
public Long getSessionId() {
return sessionId;
}
}
SessionKeyExchanger Class
public class SessionKeyExchangerService {
private static SessionKeyExchangerService exchanger;
private AESCipherService cipherService;
@Inject
public SessionKeyExchangerService(AESCipherService cipherService) {
this.cipherService = cipherService;
}
public void sendEncryptedKeyToServer () {
// the next line is almost certainly part of the problem
// but I don't know how to fix!!!
AppComponent component = DaggerAppComponent.builder().appModule(new AppModule()).build();
AESCipherService cipherService = component.provideCipherService();
Long sessionID = cipherService.getSessionId();
Log.d(Constants.TAG, "singleton verification: " + (Long.toString(sessionID)));
}
Here is some sample output:
Session ID: 217186720
singleton verification: 790090968
Clearly I'm not accessing the same object. I realize that at least part of the part of the issue stems from the way that I call the new
operator in the AESCipherService
when I am attempting to get a reference to the AppComponent
class, but I don't know how to get this reference any other way.
How do I fix this? Thanks!
Upvotes: 5
Views: 2192
Reputation: 81539
AppComponent component = DaggerAppComponent.builder().appModule(new AppModule()).build();
Nononononono. That won't be a Singleton. Scoped providers work only per component, which means you must use a single component across your application to have @Singleton
scoped modules that actually share the same scoped provider. In this case, you'd be creating a new component each time your activity is created.
You need to create them like this:
public enum Injector {
INSTANCE;
private AppComponent appComponent;
static {
INSTANCE.appComponent = DaggerAppComponent.create();
}
public getAppComponent() {
return appComponent;
}
}
You could also subclass Application
and create one there in onCreate()
.
Also
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
SessionKeyExchangerService provideSessionKeyExchangerService();
AESCipherService provideCipherService();
void inject(SplashScreenActivity splashScreenActivity); //does NOT support base class injection! Concrete classes only!
}
Then
public class SplashScreenActivity extends AppCompatActivity {
@Inject SessionKeyExchangerService exchangerService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
Injector.INSTANCE.getAppComponent().inject(this);
// establish the session id as a singleton object
// exchangerService = component.provideSessionKeyExchangerService(); //totally not needed
// test whether I can access the singleton from another class
exchangerService.sendEncryptedKeyToServer();
Also, you're using @Module
-based instance creation, so lose the @Inject
on your constructor in
@Inject
public SessionKeyExchangerService(AESCipherService cipherService) {
this.cipherService = cipherService;
}
And also
public void sendEncryptedKeyToServer () {
// the next line is almost certainly part of the problem
// but I don't know how to fix!!!
//AppComponent component = DaggerAppComponent.builder().appModule(new AppModule()).build(); //you don't need this here at all
//AESCipherService cipherService = component.provideCipherService(); //already provided in constructor
Upvotes: 3