匕首2活动停止时的保存和恢复状态
我处于僵局。我使用Dagger 2进行依赖注入,但是当应用程序进入后台时我正在丢失状态。这是场景:应用程序启动并创建依赖关系。只要应用停留在前台,所有功能都可以完美运行。但是,有一种情况是应用程序必须进入后台。当它返回时,存储在我的注入类中的值将丢失。匕首2活动停止时的保存和恢复状态
对于我的注入类没有自己的依赖关系,一切似乎都能正确恢复。但是,有一个注入的类具有注入依赖性,并且这是不能恢复的类。下面是我设置它:
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);
}
}
,然后当我去注入这些依赖关系,我这样做像这样:
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());
}
}
所以,我的根本问题是如何保持Dagger 2注入类的状态。我很高兴分享更多的代码,但这是必不可少的想法。
感谢您的任何帮助。
编辑 假设我上面所做的是好的,让我继续讨论如何保存和检索存储在这些注入对象中的值。这将表明某处存在问题。
当我进入背景,然后回来,我可以看到我得到一个新的PID。我还可以看到,我能够在LoginActivity类中正确存储和检索注入值。然而,其他类也参考注入值现在有不同的值这意味着他们的参考是一个不同的内存位置,对吗?
我最好的猜测是我在哪里出错,在LoginActivity onCreate中,我正在恢复已保存宗地中的sessionKeyExchangerService
值。我认为我正在创建新的值,这些值在应用程序中无法识别为注入依赖项,但我不知道为什么这是错误的或者如何解决它。
此代码也是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());
}
这里是控制台输出,说明了问题。通知1)onStop()
后的PID如何变化被调用,并且2)类如何Authenticator
(其具有到AESCipherService
参考具有不同的会话密钥值:
1398年至1398年/ com.mysite.myapp d/MYTAG:保存实例状态
1398-1398/com.mysite.myapp D/MYTAG:保存会话密钥: 93Zuy8B3eos + eCfBQk9ErA ==
1398-1398/com.mysite。MYAPP d/MYTAG:上 停止
3562-3562/com.mysite.myapp d/MYTAG:在 会话密钥检索上创建:93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp d/MYTAG:在开始
3562-3562/com.mysite.myapp D/MYTAG:会话密钥 检索恢复状态:93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp D/MYTAG:authenticator类说, 会话密钥是: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));
}
注入价值意味着,你不自行分配的值。这就是说,
@Inject
SessionKeyExchangerService sessionKeyExchangerService;
// then in onCreate() after the injection
sessionKeyExchangerService = savedInstanceState.getParcelable(SESSION_KEY_PARCEL);
是不是你想要做的。
如果您的SessionKeyExchangerService
取决于某些保存的状态,则必须将其传递到您的模块中。
AppModule
似乎是错误的地方提供SessionKeyExchangerService
。你应该外包给一些SessionModule
然后你可以交换,因为我认为是well explained here。在此示例中,UserModule生命周期由应用程序管理,而不是匕首。
通过提供带构造函数的模块,您可以从savedInstanceState
传递Parcelable
状态。
不知道你的整个项目,我认为你可以大大降低复杂性,可能不应该在活动中保存状态,而是使用SharedPreferences
或纯文件。这也将消除使用您的活动状态来维护模块生命周期的需要。
谢谢。 'SessionKeyExchangerService'实际上没有状态,除了依赖于'AESCipherService'。我最初选择依赖注入作为创建线程安全的单例对象的一种方式。我不能使用'SharedPreferences',因为这会迫使我将加密密钥存储在磁盘上,并且我希望这些在程序执行时离开。我没有预见到当我开始时我会创建一个有状态的环境,或者我可能只是将它放在一个静态方法的类中。我找到了一个解决方法,但它有点哈克。我可能不得不重构我的代码 – Alex
最终取消这些依赖关系。我认为我已经得出结论认为,在这种情况下依赖注入并不是理想的架构决策。生活和学习...感谢您的答案。 – Alex
您能否提供注入活动的代码以及您在何处构建图表? – Ognyan
我只是终于回到这个问题。我重新编写了这个问题,重点关注如何设置依赖注入而不是可配置的。我非常自信,我正确地做了这个部分,所以错误必须在Dagger 2部分。 – Alex
我相信你需要通过'onSaveInstanceState()'将你在模块中提供的状态序列化,并将它重新写入'onRestoreInstanceState()',考虑到进程死亡会杀死整个应用程序进程并且只有这些bundle才能存活。你可能需要在'onRestoreInstanceState()'中构造你的组件,如果它不存在,并且通过组件的提供方法实例化你的依赖关系,就可以将它重新放回一次。 – EpicPandaForce