2012-06-14 23 views
17

Ho lottato con questo per un po '. Fondamentalmente, voglio avere due applicazioni (che saranno sempre installate insieme) condividono le preferenze, con uno di loro è solo un servizio che viene eseguito in background e deve usare le preferenze (dovrebbe possedere le preferenze ma solo in realtà ha bisogno di leggerli) e l'altra è un'app di interfaccia utente front-end che deve essere in grado di scrivere sul file delle preferenze di proprietà dell'altra app. Il servizio farà le cose in background (che possono essere determinate dalle preferenze) e l'interfaccia utente consentirà all'utente di modificare le preferenze e visualizzare alcune informazioni dal servizio. Tuttavia, saranno diversi pacchetti/app.Come posso condividere un file SharedPreferences su due diverse app Android?

Ho provato a seguire this tutorial che mi ha dato una buona idea di come avere le preferenze in una app che può essere letta da un'altra. In sostanza, creo un nuovo contesto tramite myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); e quindi chiamo myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); Tuttavia, non riesco a scrivere correttamente le preferenze dall'app esterna - (SharedPreferences.Editor). Com() restituisce false e ottengo un avviso in logcat sull'impossibilità per modificare pref_name.xml.bak.

Come posso impostare correttamente le mie applicazioni in modo che entrambi possano leggere e scrivere sullo stesso file delle preferenze (che è memorizzato nella cartella dei dati di una di esse)?

+0

Perché, quando dici che questi sono sempre installati insieme, questi devono essere due app diverse? Sembra un sacco di mal di testa, quindi presumibilmente si prevede un vantaggio, ma mi chiedo se ci potrebbe essere un modo per raggiungerlo senza avere app separate. –

+2

Volevo essere in grado di installare entrambe le app, impostare le preferenze e quindi disinstallare l'interfaccia utente di frontend ma lasciare che il servizio continuasse in background. Questo mi consente anche di aggiornare una app senza reinstallare entrambe. Non è sicuramente _necessary_ (e potrei anche non usarlo) ma è stata una delle mie idee per implementarlo nel modo in cui l'ho progettato. – matt5784

risposta

6

In primo luogo, dovrei notare che questo non è ufficialmente supportato, sebbene ci possa essere un modo supportato per farlo (cioè NON sarebbe questo metodo) aggiunto ad Android in futuro (fonte per entrambe le affermazioni: vedere il secondo paragrafo di this link).

Ancora, questo non è supportato ed è molto probabilmente instabile. L'ho fatto principalmente come esperimento per vedere se fosse possibile; fare molta attenzione se si prevede di incorporare questo metodo in un'applicazione.

Tuttavia, sembra possibile condividere le preferenze tra le applicazioni se sono soddisfatti alcuni requisiti. Innanzitutto, se vuoi che l'App B sia in grado di accedere alle preferenze dell'App A, il nome del pacchetto dell'App B deve essere figlio del nome del pacchetto dell'App A (ad esempio App A: com.example.pkg App B: com.example.pkg.stuff). Inoltre, non possono volere accedere al file contemporaneamente (presumo che si applichino le stesse regole per accedervi tra le attività, se si vuole assicurare l'accesso atomico si dovranno usare misure di sicurezza aggiuntive come .wait () e .notify(), ma non entrerò in questo qui).

Nota: tutto questo funziona sull'emulatore su 2.2 e 2.3.3- Non ho testato estesamente su dispositivi o versioni di Android.




Cose da fare in app che sta andando a possedere le preferenze (App A dall'alto):

1.) dichiarare il file SharedPreferences
Questo è abbastanza semplice . Dichiara semplicemente un paio di variabili per il tuo file sharedpreferences e l'editor della classe e crea un'istanza nel tuo metodo onCreate. Puoi mettere una stringa nelle preferenze ora che userai per assicurarti che l'altra app possa leggerla correttamente.

public class stuff extends Activity { 
    SharedPreferences mPrefs = null; 
    SharedPreferences.Editor mEd= null; 
    @Override 
    public void onCreate(Bundle savedInstanceState){ 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     mPrefs = (getApplicationContext()).getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); 
     mEd = mPrefs.edit(); 
     mEd.putString("test", "original send from prefs owner"); 
     mEd.commit(); 

2.) Impostare il file di backup Il metodo getSharedPreferences sembra verificare la presenza di un file .bak per caricare le preferenze da.Questo è il motivo per cui nella documentazione afferma che non funzionerà su più processi; per minimizzare I/O, carica i prefs una sola volta quando li prendi e li ripristina solo quando chiudi la tua applicazione/attività. Tuttavia, se lo chiami da un'applicazione esterna, riceverai un avviso sul fatto di non avere i permessi file appropriati per la cartella (che è la prima cartella dati dell'app). Per risolvere questo problema creeremo il file .bak e rendiamolo pubblicamente leggibile/scrivibile. Il modo in cui ho scelto di farlo è stato quello di definire tre variabili nella mia classe generale.

final String[] copyToBackup = { "dd", "if=/data/data/com.example.pkg/shared_prefs/prefs.xml", "of=/data/data/com.example.pkg/shared_prefs/prefs.xml.bak", "bs=1024" }; 
final String[] mainFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml"}; 
final String[] bakFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml.bak"}; 

e fare una funzione nella mia classe principale che avrebbe preso questi come argomenti e eseguirli

public void execCommand(String[] arg0){ 
    try { 
     final Process pr = Runtime.getRuntime().exec(arg0); 
     final int retval = pr.waitFor(); 
     if (retval != 0) { 
      System.err.println("Error:" + retval); 
     } 
    } 
    catch (Exception e) {} 
} 

Non è terribilmente bello o buono, ma funziona. Ora, nel tuo metodo onCreate (subito dopo editor.commit()) chiamerai questa funzione con ognuna delle tre stringhe.

execCommand(copyToBackup); 
execCommand(mainFixPerm); 
execCommand(bakFixPerm); 

Questo copierà il file e fare sia la .xml principale ei file .xml.bak accessibili a programmi esterni. Dovresti anche chiamare questi tre metodi nel tuo onDestroy() per assicurarti che il database venga eseguito correttamente al momento della chiusura dell'app e chiamarli anche prima di chiamare getSharedPreferences altrove nella tua applicazione (altrimenti caricherà il file .bak che è probabilmente obsoleto se un altro processo ha modificato il file .xml principale). Questo è tutto ciò che devi fare in questa applicazione, però. Puoi chiamare getSharedPreferences altrove in questa attività e prenderà tutti i dati dal file .xml, permettendoti di chiamare i metodi getdatatype ("chiave") e recuperarli.


cose da fare nel file che accede (s) (App B dall'alto)

1.) scrivere sul file
Questo è ancora più semplice. Ho creato un pulsante su questa attività e ho impostato il codice nel suo metodo onClick che salverebbe qualcosa nel file delle preferenze condivise. Ricorda che il pacchetto di App B deve essere un figlio del pacchetto dell'App A. Creeremo un contesto basato sul contesto dell'app A e quindi chiameremo getSharedPreferences su quel contesto.

prefsbutton.setOnClickListener(new View.OnClickListener() { 
    public void onClick(View v) { 
     Context myContext = null; 
     try { 
      // App A's context 
      myContext = createPackageContext("com.example.pkg", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); 
     } catch (NameNotFoundException e) {e.printStackTrace();} 

     testPrefs = myContext.getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); 
     testEd = testPrefs.edit(); 
     String valueFromPrefs = testPrefs.getString("test", "read failure"); 
     TextView test1 = (TextView)findViewById(R.id.tvprefs); 
     test1.setText(valueFromPrefs); 
     testEd.putString("test2", "testback"); 
     boolean edit_success = testEd.commit(); 

Questa afferra la stringa ho impostato nell'altra applicazione e lo visualizza (o un messaggio di errore) in un TextView in questa applicazione. Inoltre imposta una nuova stringa nel file delle preferenze e conferma le modifiche. Al termine dell'esecuzione, se l'altra applicazione chiama getSharedPreferences, recupererà il file incluse le modifiche da questa app.

+2

"anche se potrebbe essere in futuro" - l'utilizzo dei binari della riga di comando non sarà mai supportato nell'SDK di Android. Personalmente, non vorrei toccare questa tecnica con un palo di 10 metri. Se si desidera condividere dati tra le app, utilizzare un'API reale, ad esempio l'implementazione di un ContentProvider supportato da 'SharedPreferences'. – CommonsWare

+0

Non potevo prevedere cosa farà la squadra di android (tranne quando me lo dicono). Tuttavia, sono ** abbastanza ** sicuro che se lo implementassero sarebbe molto più pulito e non richiederebbe l'esecuzione di 'dd' o' chmod'. In realtà, questo potrebbe anche essere possibile attraverso qualche altra tecnica - e se è spero che qualcuno pubblicherà una risposta. Questo è solo il modo in cui ho potuto farlo funzionare. – matt5784

+0

È possibile che due app scrivano dati sulle stesse sharedprefrence? –

34

È preferibile impostare la modalità privata per il file. L'app deve essere firmata con lo stesso set di certificati per condividere questo file.

Impostare sharedUserId in entrambe le app per essere uguale.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="com.example.hello" 
android:versionCode="1" 
android:versionName="1.0" 
android:sharedUserId="com.example"> 
.... 

ottenere il contesto da altro pacchetto:

mContext = context.createPackageContext(
         "com.example.otherapp", 
         Context.MODE_PRIVATE); 
mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE); 

ottenere elementi come al solito da SharedPreference. Puoi accedervi ora.

+2

Non so perché questa non è la risposta accettata. Questo è il modo corretto per farlo e funziona perfettamente. –

+1

Inoltre potrebbe essere necessario sostituire 'Context.MODE_PRIVATE' con' Context.CONTEXT_RESTRICTED'. –

+0

Questo è facile da implementare, tuttavia, sfortunatamente, se lo aggiungi in una fase successiva dello sviluppo, l'aggiornamento della tua app non funzionerà (devi reinstallare). Inoltre, se utilizzi la fatturazione in-app, è molto probabile che non funzioni. – lenooh

Problemi correlati