【问题标题】:Dagger 2 Save and Restore State when Activity StopsDagger 2 Activity 停止时保存和恢复状态
【发布时间】:2016-02-08 19:47:52
【问题描述】:

我陷入了僵局。我正在使用 Dagger 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:验证器类说 会话密钥是: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 ));
}

【问题讨论】:

  • 您能否提供注入活动的代码以及构建图表的位置?
  • 我终于回到了这个问题。我重新编写了这个问题,专注于我如何设置依赖注入而不是 parcelables。我很有信心我正确地完成了那部分,所以错误必须在 Dagger 2 部分。
  • 我相信您需要通过onSaveInstanceState() 序列化您在模块中提供的内容的状态,然后将其返回到onRestoreInstanceState(),考虑到进程死亡会杀死整个应用程序进程并且只有捆绑包才能存活.如果您的组件不存在,您可能需要在 onRestoreInstanceState() 中构建它,然后通过组件的提供方法实例化您的依赖项,将其放回原处。
  • 是的,我通过onSaveInstanceState() 保存了我的值。您能否通过“通过组件的提供方法实例化您的依赖项将其放回一次”来进一步解释您的意思。我相信这是我出错的地方,但我不知道如何解决这个问题。我扩展了我的问题以显示我在这里做什么。谢谢!
  • 什么是Injector 可以解释一下这行吗Injector.INSTANCE.getAppComponent().inject(this);

标签: android state activity-lifecycle dagger-2


【解决方案1】:

注入值意味着您不会自己分配值。这就是说,

@Inject 
SessionKeyExchangerService sessionKeyExchangerService;

// then in onCreate() after the injection
sessionKeyExchangerService = savedInstanceState.getParcelable(SESSION_KEY_PARCEL);

不是你想做的。

如果您的 SessionKeyExchangerService 依赖于某些已保存的状态,则必须将其传递到您的模块中。

AppModule 似乎是提供SessionKeyExchangerService 的错误位置。你可能应该外包给一些SessionModule,然后你可以交换,我认为是well explained here。在此示例中,UserModule 生命周期由应用程序管理,而不是 dagger。

通过为模块提供构造函数,您可以从savedInstanceState 传入Parcelable 状态。

在不了解整个项目的情况下,我认为您可以大大降低复杂性,并且可能不应该在活动中保存状态,而是使用SharedPreferences 或普通文件。这也将消除使用您的活动状态维护模块生命周期的需要。

【讨论】:

  • 谢谢。 SessionKeyExchangerService 实际上没有状态,除了它对 AESCipherService 的依赖。我最初选择依赖注入作为创建线程安全的单例对象的一种方式。我不能使用SharedPreferences,因为这样做会迫使我将加密密钥存储在磁盘上,并且我希望这些在程序执行时消失。当我开始时,我没有预见到我会创建一个有状态的情况,或者我可能只是把它放在一个带有静态方法的类中。我找到了解决方法,但这有点hacky。我可能需要重构我的代码
  • 最终拉出这些依赖关系。我想我已经得出结论,在这种情况下,依赖注入并不是理想的架构决策。生活和学习...感谢您的回答。
猜你喜欢
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 2019-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
相关资源
最近更新 更多