I'm at an impasse. I'm using Dagger 2 for dependency injection, but I'm losing state when the app goes into the background. Here is the scenario: the app starts up and creates the dependencies. All works perfectly as long as the app stays in the foreground. However, there is a scenario when the app has to go into the background. When it comes back, the values stored in one of my injected classes are lost.
For my injected classes that have no dependencies of their own, everything seems to recover correctly. However, there is one injected class that has an injected dependency, and this is the one that doesn't recover. Here is how I am setting it up:
AppComponent.java
@Singleton
@Component(
modules = {
AppModule.class
}
)
public interface AppComponent {
SessionKeyExchangerService provideSessionKeyExchangerService();
AESCipherService provideCipherService();
void inject(LoginActivity loginActivity);
}
AppModule.java
@Module
public class AppModule {
@Provides @Singleton
AESCipherService provideCipherService() {
return new AESCipherService();
}
@Provides @Singleton
SessionKeyExchangerService provideSessionKeyExchangerService(AESCipherService service) {
return new SessionKeyExchangerService(service);
}
}
And then when I go to inject these dependencies, I do it like this:
LoginActivity.java
@Inject
SessionKeyExchangerService sessionKeyExchangerService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Injector.INSTANCE.getAppComponent().inject(this);
if (savedInstanceState != null) {
sessionKeyExchangerService = savedInstanceState.getParcelable(SESSION_KEY_PARCEL);
Log.d(Constants.TAG, "session key retrieved in on create: " + sessionKeyExchangerService.getCipherService().getBase64EncodedSessionKey());
}
}
So, my fundamental question is how to maintain the state of a Dagger 2 injected class. I'm happy to share more code, but this is the essential idea.
Thanks for any help.
EDIT Assuming that what I've done above is OK, let me move on to how I save and retrieve the values stored in those injected objects. This will show that there is a problem somewhere.
When I go into the background, and then come back, I can see that I get a new PID. I can also see that I am able to store and retrieve the injected values correctly in the LoginActivity class. However, other classes that also have a reference to the injected value now have different values meaning that their reference is to a different memory location, right?
My best guess as to where I am going wrong is in LoginActivity onCreate where I am restoring the sessionKeyExchangerService
value from the saved parcel. I think I am creating new values that are not recognized across the app as injected dependencies, but I don't know why this is wrong or how to fix it.
This code is also in LoginActivity.java:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(SESSION_KEY_PARCEL, sessionKeyExchangerService);
Log.d(Constants.TAG, "session key saved: " + sessionKeyExchangerService.getCipherService().getBase64EncodedSessionKey());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
sessionKeyExchangerService = savedInstanceState.getParcelable(SESSION_KEY_PARCEL);
Log.d(Constants.TAG, "session key retrieved in on restore state: " + sessionKeyExchangerService.getCipherService().getBase64EncodedSessionKey());
}
Here is the console output that illustrates the issue. Notice 1) how the PID changes after onStop()
is called, and 2) how the class Authenticator
(which has a reference to AESCipherService
has a different session key value:
1398-1398/com.mysite.myapp D/MYTAG﹕ on save instance state
1398-1398/com.mysite.myapp D/MYTAG﹕ session key saved: 93Zuy8B3eos+eCfBQk9ErA==
1398-1398/com.mysite.myapp D/MYTAG﹕ on stop
3562-3562/com.mysite.myapp D/MYTAG﹕ session key retrieved in on create: 93Zuy8B3eos+eCfBQk9ErA==
3562-3562/com.mysite.myapp D/MYTAG﹕ on start
3562-3562/com.mysite.myapp D/MYTAG﹕ session key retrieved in on restore state: 93Zuy8B3eos+eCfBQk9ErA==
3562-3562/com.mysite.myapp D/MYTAG﹕ authenticator class says that the session key is: 28HwdRCjBqH3uFweEAGCdg==
SessionKeyExchangerService.java
protected SessionKeyExchangerService(Parcel in) {
notifyOn = in.readString();
sessionKeyExchangeAttempts = in.readInt();
MAX_SESSION_KEY_EXCHANGE_ATTEMPTS = in.readInt();
sessionKeyExchangeHasFailed = (in.readByte() == 1);
cipherService = in.readParcelable(AESCipherService.class.getClassLoader());
}
public static final Creator<SessionKeyExchangerService> CREATOR = new Creator<SessionKeyExchangerService>() {
@Override
public SessionKeyExchangerService createFromParcel(Parcel in) {
return new SessionKeyExchangerService(in);
}
@Override
public SessionKeyExchangerService[] newArray(int size) {
return new SessionKeyExchangerService[size];
}
};
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(notifyOn);
dest.writeInt(sessionKeyExchangeAttempts);
dest.writeInt(MAX_SESSION_KEY_EXCHANGE_ATTEMPTS);
dest.writeByte((byte) (hasSessionKeyExchangeFailed() ? 1 : 0));
dest.writeParcelable(cipherService, flags);
}
AESCipherService.java
protected AESCipherService(Parcel in) {
sessionKeyBytes = in.createByteArray();
ivBytes = in.createByteArray();
sessionId = in.readLong();
mIsSessionKeyEstablished = (in.readByte() == 1);
verbose = (in.readByte() == 1);
}
public static final Creator<AESCipherService> CREATOR = new Creator<AESCipherService>() {
@Override
public AESCipherService createFromParcel(Parcel in) {
return new AESCipherService(in);
}
@Override
public AESCipherService[] newArray(int size) {
return new AESCipherService[size];
}
};
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(sessionKeyBytes);
dest.writeByteArray(ivBytes);
dest.writeLong(sessionId);
dest.writeByte((byte) (isSessionKeyEstablished() ? 1 : 0));
dest.writeByte((byte) (verbose ? 1 : 0 ));
}
onSaveInstanceState()
and hack it back inonRestoreInstanceState()
, considering process death kills the entire application process and only the bundles survive. You'd probably need to construct your component inonRestoreInstanceState()
if it doesn't exist, and put it back in once by instantiating your dependencies through the component's provision methods. – ErythropoiesisonSaveInstanceState()
. Could you explain more about what you mean by 'put it back in once by instantiating your dependencies through the component's provision methods'. I believe that this is where I am going wrong, but I don't know how to fix this. I expanded my question to show what I am doing here. Thank you! – RecruitmentInjector
can you please explain this lineInjector.INSTANCE.getAppComponent().inject(this);
– Karleen