2014-10-16 15 views
13

Sto modificando e inserendo un SharedPreference nel mio SyncAdapter dopo una corretta sincronizzazione, ma non vedo il valore aggiornato quando accedo alla preferenza nella mia attività (piuttosto sto vedendo il vecchio valore). Che cosa sto facendo di sbagliato? Contesti diversi?SharedPreference commesso in SyncAdapter non aggiornato in Attività?

mio SyncAdapter dove posso aggiornare la preferenza:

class SyncAdapter extends AbstractThreadedSyncAdapter { 
    private int PARTICIPANT_ID; 
    private final Context mContext; 
    private final ContentResolver mContentResolver; 

    public SyncAdapter(Context context, boolean autoInitialize) { 
     super(context, autoInitialize); 
     mContext = context; 
     mContentResolver = context.getContentResolver(); 
    } 

    public SyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) { 
     super(context, autoInitialize, allowParallelSyncs); 
     mContext = context; 
     mContentResolver = context.getContentResolver(); 
    } 

    @Override 
    public void onPerformSync(Account account, Bundle extras, String authority, 
           ContentProviderClient provider, SyncResult syncResult) { 

     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); 
     PARTICIPANT_ID = Integer.parseInt(prefs.getString("participant_id", "0")); 

     if (success) { 
      // save and set the new participant id 
      PARTICIPANT_ID = newParticipantId; 
      prefs.edit().putString("participant_id", String.valueOf(newParticipantId)).commit(); 
     } 
    } 
} 

Il Servizio inizializzazione del SyncAdapter con l'ApplicationContext:

public class SyncService extends Service { 
    private static final Object sSyncAdapterLock = new Object(); 
    private static SyncAdapter sSyncAdapter = null; 

    @Override 
    public void onCreate() { 
     synchronized (sSyncAdapterLock) { 
      if (sSyncAdapter == null) { 
       sSyncAdapter = new SyncAdapter(getApplicationContext(), false); 
      } 
     } 
    } 

    @Override 
    public IBinder onBind(Intent intent) { 
     return sSyncAdapter.getSyncAdapterBinder(); 
    } 
} 

Una funzione statica all'interno della applicazione chiamata dall'attività che controlla lo SharedPreference. Questo non restituisce il valore impegnato in SyncAdapter, ma il vecchio valore. (Il mio SettingsActivity e altre attività anche utilizzare il vecchio valore.):

public static boolean isUserLoggedIn(Context ctx) { 
    final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); 
    int participantId = Integer.parseInt(prefs.getString("participant_id", "0")); 
    LOGD("dg_Utils", "isUserLoggedIn.participantId: " + participantId);// TODO 
    if (participantId <= 0) { 
     ctx.startActivity(new Intent(ctx, LoginActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); 
     return false; 
    } 
    return true; 
} 

UPDATE: Mi è toccato il nuovo valore quando ho chiudere completamente l'applicazione (falla scorrere dal applicazioni in esecuzione). Ho anche un SharedPreferenceChangeListener, che non viene attivato quando la preferenza viene aggiornata.

private final SharedPreferences.OnSharedPreferenceChangeListener mParticipantIDPrefChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 
    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 
     if (key.equals("participant_id")) { 
      LOGI(TAG, "participant_id has changed, requesting to restart the loader."); 
      mRestartLoader = true; 
     } 
    } 
}; 

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 

    // subscribe to the participant_id change lister 
    final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 
    PARTICIPANT_ID = Integer.parseInt(prefs.getString("participant_id", "0")); 
    prefs.registerOnSharedPreferenceChangeListener(mParticipantIDPrefChangeListener); 
} 
+0

Dal codice che hai postato sembra che non hai mai chiamare il 'startSync()' 'sul SyncAdapter' il che significa che il metodo' onPerformSync (...) 'non viene eseguita e il la preferenza non è mai cambiata. – Titus

+0

@Titus Da qualche parte faccio e 'onPerformSync()' è definitivamente eseguito. Utilizza anche il nuovo valore nelle sincronizzazioni successive. Ho scoperto che 'SyncService' è in esecuzione in un processo separato (' '). Ora sto cercando di capire come usare 'MODE_MULTI_PROCESS' con' PreferenceManager.getDefaultSharedPreferences() '. – matsch

+0

Puoi usare 'prefs = mContext.getSharedPreferences (" myAppPrefs ", Context.MODE_MULTI_PROCESS);' [Qui] (http://developer.android.com/reference/android/content/Context.html#getSharedPreferences (java.lang .String, int)) è la documentazione. – Titus

risposta

16

Ok, ho capito me stesso con @Titus aiuto e dopo qualche ricerca e messo insieme una soluzione per il mio problema.

Il motivo per cui la DefaultSharedPreferences dello stesso Context non vengono aggiornati è che ho specificato il SyncService per l'esecuzione in proprio processo nel AndroidManifest.xml (vedi sotto). Quindi, a partire da Android 2.3, l'altro processo non può accedere al file SharedPreferences aggiornato (vedere this answer e i documenti Android su Context.MODE_MULTI_PROCESS).

<service 
     android:name=".sync.SyncService" 
     android:exported="true" 
     android:process=":sync" 
     tools:ignore="ExportedService" > 
     <intent-filter> 
      <action android:name="android.content.SyncAdapter" /> 
     </intent-filter> 
     <meta-data 
      android:name="android.content.SyncAdapter" 
      android:resource="@xml/syncadapter" /> 
    </service> 

così ho dovuto impostare MODE_MULTI_PROCESS quando si accede al SharedPreferences sia nel SyncAdapter e nel processo di interfaccia utente di mia app. Poiché ho utilizzato PreferenceManager.getDefaultSharedPreferences(Context) in modo esteso in tutta l'app, ho scritto un metodo di utilità e ho sostituito tutte le chiamate di PreferenceManager.getDefaultSharedPreferences(Context) con questo metodo (vedi sotto). Il nome predefinito del file delle preferenze è hardcoded e derivato da the Android source code e this answer.

public static SharedPreferences getDefaultSharedPreferencesMultiProcess(
     Context context) { 
    return context.getSharedPreferences(
      context.getPackageName() + "_preferences", 
      Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS); 
} 
+0

Secondo http://stackoverflow.com/a/6310080/8524 il codice sopra riportato otterrà lo stesso oggetto 'SharedPreferences' come' SharedPreferences.getDefaultSharedPreferences() 'fa . – Diederik

+1

Sì, otterrà lo stesso oggetto, ma con il modificatore 'Context.MODE_MULTI_PROCESS' impostato. Sto collegando la stessa risposta che hai nel mio ultimo paragrafo. – matsch

+0

Buona aggiunta alla tua risposta, penso. Ho aggiunto il commento nel caso in cui qualcuno (come me) usasse 'SharedPreferences.getDefaultSharedPreferences()' per ottenere le preferenze condivise, e quindi realizzare che hanno bisogno di un accesso multi processo. – Diederik

0

Dal momento che non sono SharedPreferences processo di sicurezza, non consiglierei di utilizzare l'AbstractThreadedSyncAdapter in un altro processo a meno che non si ha realmente bisogno.

Why do i need multiple processes in my application?

Soluzione

Rimuovere android:process=":sync" dal servizio che sono stati dichiarati nel manifesto!

+0

Grazie ha funzionato per me. –

0

Nel mio caso stavo cercando di accedere a SharedPreferences da un servizio lanciato da BroadcastReceiver.

Ho rimosso android:process=":remote" dalla dichiarazione in AndroidManifest.xml per farlo funzionare.

<receiver 
    android:process=":remote" 
    android:name=".Alarm"> 
</receiver>