2015-11-07 3 views
8

Я в тупике. Я использую 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 нагнетаемый класса. Я рад поделиться больше кода, но это основная идея.

Спасибо за любую помощь.

EDIT Предполагая, что я сделал выше в порядке, позвольте мне перейти к тому, как я сохранять и извлекать значения, хранящиеся в этих инжектированных объектов. Это покажет, что есть проблема где-то.

Когда я ухожу на задний план, а затем возвращаюсь, я вижу, что получаю новый 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), как изменяется PID после onStop() называется, и 2), как класс Authenticator (который имеет ссылку на AESCipherService имеет другое значение ключа сеанса:

1398-1398/com.mysite.myapp Д/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)); 
} 
+1

Можете ли вы предоставить код, в котором вы вводите свою деятельность, а также где вы строите график? – Ognyan

+0

Я как раз возвращаюсь к этому вопросу. Я переписал вопрос, чтобы сосредоточиться на том, как я настроил инъекцию зависимостей, а не сравнительные. Я довольно уверен, что правильно делаю эту часть, поэтому ошибка должна быть в части Dagger 2. – Alex

+0

Я считаю, что вам нужно будет сериализовать состояние того, что вы предоставляете в своем модуле, с помощью 'onSaveInstanceState()' и взломать его обратно в 'onRestoreInstanceState()', учитывая, что процесс смерти убивает весь процесс подачи заявки, и выживают только пакеты. Вероятно, вам нужно будет создать свой компонент в 'onRestoreInstanceState()', если он не существует, и вернуть его один раз, создав свои зависимости через методы предоставления компонентов. – EpicPandaForce

ответ

1

инъекционные значения означает, что вы не присвоит значение самостоятельно. Это указано,

@Inject 
SessionKeyExchangerService sessionKeyExchangerService; 

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

не то, что вы хотите.

Если ваш SessionKeyExchangerService зависит от некоторого сохраненного состояния, вам необходимо передать его в свой модуль.

AppModule представляется неправильным местом для предоставления SessionKeyExchangerService. Вероятно, вы должны передать на аутсорсинг примерно SessionModule, который затем вы можете обменять, так как я думаю, это well explained here. В этом примере жизненный цикл UserModule управляется приложением, а не кинжалом.

Предоставляя модуль с конструктором, вы можете передать в своем Parcelable состоянии из savedInstanceState.

Не зная весь проект, я думаю, что вы можете значительно уменьшить сложность и, вероятно, не должны сохранять состояние в своей деятельности, а использовать SharedPreferences или простые файлы. Это также устранит необходимость сохранения жизненного цикла модуля с состоянием активности.

+0

Спасибо. 'SessionKeyExchangerService' фактически не имеет состояния, кроме его зависимости от AESCipherService. Я изначально выбрал инъекцию зависимостей как способ создания одноэлементных объектов, которые были потокобезопасными.Я не могу использовать 'SharedPreferences', потому что выполнение заставит меня хранить криптографические ключи на диске, и я хочу, чтобы они ушли, когда программа работает. Я не предвидел, что я создам ситуацию с состоянием, когда я начну, или я, вероятно, просто поместил бы это в класс со статическими методами. Я нашел обходное решение, но это хакки. Возможно, мне придется реорганизовать мой код – Alex

+0

, чтобы вытащить эти зависимости. Я думаю, что я пришел к выводу, что инъекция зависимостей не является идеальным архитектурным решением в этом случае. Живи и учись ... Спасибо за ответ. – Alex

Смежные вопросы