2015-11-07 1 views
8

Je suis dans une impasse. J'utilise Dagger 2 pour l'injection de dépendance, mais je perds l'état lorsque l'application passe en arrière-plan. Voici le scénario: l'application démarre et crée les dépendances. Tout fonctionne parfaitement tant que l'application reste au premier plan. Cependant, il existe un scénario dans lequel l'application doit passer en arrière-plan. Quand il revient, les valeurs stockées dans l'une de mes classes injectées sont perdues.Dague 2 Enregistrer et restaurer l'état lorsque l'activité s'arrête

Pour mes classes injectées qui n'ont aucune dépendance propre, tout semble se rétablir correctement. Cependant, il y a une classe injectée qui a une dépendance injectée, et c'est celle qui ne récupère pas. Voici comment je suis mise en place:

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); 
    } 
} 

Et puis quand je vais à injecter ces dépendances, je le fais comme ceci:

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()); 
    } 
} 

Alors, ma question fondamentale est de savoir comment maintenir l'état d'un poignard 2 classe injectée. Je suis heureux de partager plus de code, mais c'est l'idée essentielle.

Merci pour toute aide.

EDIT En supposant que ce que je l'ai fait ci-dessus est OK, laissez-moi passer à comment j'enregistrer et récupérer les valeurs stockées dans les objets injectés. Cela montrera qu'il y a un problème quelque part. Lorsque je passe en arrière-plan et que je reviens, je peux voir que je reçois un nouveau PID. Je peux également voir que je suis capable de stocker et de récupérer correctement les valeurs injectées dans la classe LoginActivity. Cependant, d'autres classes qui ont également une référence à la valeur injectée ont maintenant des valeurs différentes ce qui signifie que leur référence est à un emplacement de mémoire différent, non?

Ma meilleure estimation de l'endroit où je vais mal est dans LoginActivity onCreate où je restaure la valeur sessionKeyExchangerService de la parcelle sauvegardée. Je pense que je crée de nouvelles valeurs qui ne sont pas reconnues dans l'application comme des dépendances injectées, mais je ne sais pas pourquoi c'est faux ou comment y remédier.

Ce code est également 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()); 
} 

est ici la sortie de la console qui illustre la question. Remarque 1) comment les changements PID après onStop() est appelé, et 2) comment la classe Authenticator (qui a une référence à AESCipherService a une valeur clé de session différente:

1398-1398/com.mysite.myapp D/MYTAG: à l'état d'instance de sauvegarde
1398-1398/com.mysite.myapp D/MYTAG: clé de session enregistrée: 93Zuy8B3eos + eCfBQk9ErA ==
1398-1398/com.mysite.myapp D/MYTAG: sur arrêt
3562-3562/com.mysite.myapp D/MYTAG: clé de session récupérée dans sur la création: 93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp D/MYTAG: au démarrage
3562-3562/com.mysite.myapp D/MYTAG: clé de session récupérée dans le état de restauration: 93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp D/MYTAG: classe authentificateur dit que le clé de session est: 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

Pouvez-vous indiquer le code dans lequel vous injectez votre activité et où construisez-vous le graphique? – Ognyan

+0

Je reviens finalement à cette question. J'ai réécrit la question pour me concentrer sur la façon dont j'ai mis en place l'injection de dépendance plutôt que sur les parcelables. Je suis assez confiant que je fais cette partie correctement, donc l'erreur doit être dans la partie Dagger 2. – Alex

+0

Je crois que vous devriez sérialiser l'état de ce que vous fournissez dans votre module par 'onSaveInstanceState()' et le hacker dans 'onRestoreInstanceState()', considérant que la mort du processus tue tout le processus applicatif et que seuls les bundles survivent. Vous devrez probablement construire votre composant dans 'onRestoreInstanceState()' s'il n'existe pas, et le remettre en place une fois en instanciant vos dépendances à travers les méthodes de provisionnement du composant. – EpicPandaForce

Répondre

1

valeurs signifie Injecter, que vous ne donnez pas la valeur vous-même. Cela dit,

@Inject 
SessionKeyExchangerService sessionKeyExchangerService; 

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

n'est pas ce que vous voulez faire.

Si votre SessionKeyExchangerService dépend d'un état enregistré, vous devrez le passer dans votre module.

AppModule semble être le mauvais endroit pour fournir le SessionKeyExchangerService. Vous devriez probablement externaliser à SessionModule que vous pouvez ensuite échanger, comme je pense est well explained here. Dans cet exemple, le cycle de vie UserModule est géré par l'application et non par poignard. En fournissant un module avec un constructeur, vous pouvez passer votre état Parcelable à . Sans connaître votre projet dans son ensemble, je pense que vous pouvez réduire considérablement la complexité et ne devrait probablement pas enregistrer l'état dans l'activité, mais plutôt utiliser SharedPreferences ou des fichiers simples. Cela supprimerait également le besoin de maintenir le cycle de vie du module avec votre état d'activité.

+0

Merci. 'SessionKeyExchangerService' n'a réellement aucun état, autre que sa dépendance à' AESCipherService'. J'ai initialement choisi l'injection de dépendance comme moyen de créer des objets singleton qui étaient threadsafe.Je ne peux pas utiliser 'SharedPreferences' car cela me forcerait à stocker des clés de chiffrement sur le disque, et je veux qu'elles disparaissent quand le programme le fera. Je n'avais pas prévu que je créerais une situation d'état lorsque j'aurais commencé ou je l'aurais probablement mis dans une classe avec des méthodes statiques. J'ai trouvé une solution de contournement à cela, mais c'est une sorte de hacky. Je dois refactoriser mon code – Alex

+0

éventuellement pour sortir ces dépendances. Je pense avoir conclu que l'injection de dépendance n'était pas la décision architecturale idéale dans ce cas. Vivre et apprendre ... Merci pour la réponse. – Alex