2015-11-07 20 views
8

Sono in un vicolo cieco. Sto usando Dagger 2 per l'iniezione delle dipendenze, ma sto perdendo lo stato quando l'app entra in background. Ecco lo scenario: l'app si avvia e crea le dipendenze. Tutto funziona perfettamente finché l'app rimane in primo piano. Tuttavia, esiste uno scenario in cui l'app deve andare in secondo piano. Quando ritorna, i valori memorizzati in una delle mie classi iniettate vanno persi.Dagger 2 Salva e ripristina stato quando l'attività si arresta

Per le mie classi iniettate che non hanno dipendenze proprie, tutto sembra ripristinarsi correttamente. Tuttavia, esiste una classe iniettata con una dipendenza iniettata, e questa è quella che non si ripristina. Ecco come sto sua creazione:

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

E poi, quando vado per iniettare queste dipendenze, lo faccio In questo modo:

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

Quindi, la mia domanda fondamentale è come mantenere lo stato di una classe iniettato Dagger 2. Sono felice di condividere più codice, ma questa è l'idea essenziale.

Grazie per qualsiasi aiuto.

EDIT Partendo dal presupposto che quello che ho fatto in precedenza è ok, mi permetta di andare avanti a come mi salvare e recuperare i valori memorizzati in quegli oggetti iniettati. Questo mostrerà che c'è un problema da qualche parte.

Quando vado in background, e poi torno, posso vedere che ottengo un nuovo PID. Posso anche vedere che sono in grado di memorizzare e recuperare correttamente i valori iniettati nella classe LoginActivity. Tuttavia, altre classi che hanno anche un riferimento al valore iniettato hanno ora valori diversi, il che significa che il loro riferimento è a una diversa posizione di memoria, giusto?

La mia ipotesi migliore su dove sto andando storto è in LoginActivity onCreate dove sto ripristinando il valore sessionKeyExchangerService dal pacchetto salvato. Penso di creare nuovi valori che non sono riconosciuti dall'app come dipendenze iniettate, ma non so perché questo sia sbagliato o come risolverlo.

Questo codice è anche 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()); 
} 

Ecco l'output della console che illustra il problema. Avviso 1) come cambia il PID dopo onStop() si chiama, e 2) come la classe Authenticator (che ha un riferimento a AESCipherService ha una sessione diverso valore chiave:

1398-1398/com.mysite.myapp D/MYTAG: on salva istanza
1398-1398/com.mysite.myapp D/MYTAG: chiave di sessione salvata: 93Zuy8B3eos + eCfBQk9ErA ==
1398-1398/com.mysite.myapp D/MyTag: il arresto
3562-3562/com.mysite.myapp D/MyTag: chiave di sessione recuperata in su Crea: 93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp D/MyTag: all'avvio
3562-3562/com.mysite.myapp D/MYTAG: chiave di sessione recuperata in stato di ripristino: 93Zuy8B3eos + eCfBQk9ErA ==
3562-3562/com.mysite.myapp D/MYTAG: la classe di autenticatore dice che il chiave di sessione è: 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

È possibile fornire il codice in cui si inietta l'attività e anche dove si sta costruendo il grafico? – Ognyan

+0

Sto finalmente tornando a questa domanda. Ho riscritto la domanda per concentrarmi sul modo in cui ho impostato l'iniezione della dipendenza piuttosto che sui parcelables. Sono abbastanza fiducioso che sto facendo questa parte correttamente, quindi l'errore deve essere nella parte di Dagger 2. – Alex

+0

Credo che avresti bisogno di serializzare lo stato di ciò che fornisci nel tuo modulo attraverso 'onSaveInstanceState()' e reinserirlo in 'onRestoreInstanceState()', considerando che la morte del processo uccide l'intero processo di applicazione e solo i bundle sopravvivono. Probabilmente dovresti costruire il tuo componente in 'onRestoreInstanceState()' se non esiste, e reinserirlo una volta istanziando le tue dipendenze attraverso i metodi di provisioning del componente. – EpicPandaForce

risposta

1

valori mezzi iniettori, che non si assegna il valore da soli. Detto questo,

@Inject 
SessionKeyExchangerService sessionKeyExchangerService; 

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

non è quello che vuoi fare.

Se il tuo SessionKeyExchangerService dipende da qualche stato salvato, dovrai passarlo nel tuo modulo.

AppModule sembra essere il posto sbagliato per fornire il SessionKeyExchangerService. Probabilmente dovresti esternalizzare ad alcuni SessionModule che poi puoi scambiare, come penso sia well explained here. In questo esempio, il ciclo di vita di UserModule è gestito dall'app e non da un pugnale.

Fornendo un modulo con un costruttore, è possibile passare dallo stato Parcelable allo savedInstanceState.

Senza conoscere l'intero progetto, penso che si possa ridurre notevolmente la complessità e probabilmente non si dovrebbe salvare lo stato nell'attività, ma piuttosto usare SharedPreferences o file semplici. Ciò eliminerebbe anche la necessità di mantenere il ciclo di vita del modulo con il tuo stato di attività.

+0

Grazie. 'SessionKeyExchangerService' in realtà non ha uno stato, a parte la sua dipendenza da' AESCipherService'. Inizialmente ho scelto l'integrazione delle dipendenze come un modo per creare oggetti singleton che fossero sicuri.Non posso usare 'SharedPreferences' perché fare mi costringerebbe a memorizzare le chiavi di crittografia sul disco e voglio che queste vadano via quando il programma funziona. Non avevo previsto che avrei creato una situazione di stato quando ho iniziato o probabilmente avrei messo questo in una classe con metodi statici. Ho trovato una soluzione a questo, ma è una specie di hacky. Potrei dover refactoring il mio codice – Alex

+0

alla fine per estrarre queste dipendenze. Penso di aver concluso che l'iniezione di dipendenza non era la decisione architettonica ideale in questo caso. Vivi e impara ... Grazie per la risposta. – Alex

Problemi correlati