Senza categoria

[DEV] – Shared Preferences

Ogni applicazione ha la necessità di memorizzare dei “settings”, in modo da customizzarsi in base alle esigenze dell’utente. Si pensi ad applicazioni che accedono a servizi remoti e devono memorizzare la userName e la password per evitare di richiederle ogni volta, oppure la cartella preferita dove registrare le foto, ecc ecc
Android mette a disposizione un framework per la memorizzazione dei parametri di configurazione in modo da rendere il tutto standard e … semplice.

Infatti non vengono messe a disposizione solo API per la memorizzazione delle preferenze (che poi non sono altro che un insieme di coppie chiave/valore) ma anche delle intefacce grafice (la classica voce di menù “preferences”) che sono in grado di accettare vari tipi di definizioni (vedremo dopo).

Questo rende il tutto molto semplice da sviluppare e anche molto standard, infatti tutte le applicazioni che devono avere parametri di configurazione hanno una interfaccia simile rendendo più omogenea l’esperienza utente.

SharedPreference

Purtroppo, a mio avviso, la classe Java messa a disposizione da Android ha un nome un po’ infelice per l’uso che se fa di solito. Infatti la classe java si chiama

android.content.SharedPreferences

che fa chiaramente immaginare che le preference siano sherate (cioè condivise). Questo è sicuramente vero in un caso particolare, ma in tutti gli altri casi no.

Innanzitutto la classe SharedPreference non è altro che una mappa chiave/valore adatta ad ospitare parametri di configurazione dell’applicazione e che sopravvive tra una sessione e l’altra dell’applicazione.
I dati memorizzati attraverso questa classe vengono salvati in un file XML presente nella cartella

data/data/<package dell'applicazione>/shared_prefs

Questo la rende ideale se si vogliono memorizzare dati che devono essere recuparate al prossimo uso dell’applicazione (si pensi ad esempio ad una password che viene chiesta all’avvio. Per verificare se la password è corretta bisogna confrontarla con qualcosa che sia già disponibile, senza per questo ricorrere per forza ad un database).

Sebbene la classe java per la gestione dei dati sia sempre la stessa, Android fa una differenziazione sulla tipologia si SharedPreference.

  • SharedPreference di contesto, cioè legate al contesto dell’applicazione
  • Preference di configurazione, cioè legate all’applicazione e moficabili anche attraverso una interfaccia utente. Questo tipo di Preference non possono essere “sherate” anche se la classe Java si chiama sempre SharedPreference

SharedPreference di contesto

Il concetto che sta dietro a questo tipo di sharedPreference è molto semplice. L’utente crea una sharedPrefernce (o la carica se esiste già) e ne legge i valori. Nel caso che voglia inserire i valori dovrà usare una sorta di editor (applicativo) dedicato ed effettuare il save (commit) dei dati. Ecco un esempio:

[java]

// definiamo il nome delle sharedPreference (se ne possono avere più di una)
public static final String MYPREFS = “mySharedPreferences”;

protected void savePreferences(){
// Crea o recupare la sharedPreference
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = getSharedPreferences(MYPREFS, mode);

// Crea un editor per modificare i dati
SharedPreferences.Editor editor = mySharedPreferences.edit();

// Memorizza i dati primitivi
editor.putBoolean(“isTrue”, true);
editor.putFloat(“lastFloat”, 1f);
editor.putInt(“wholeNumber”, 2);
editor.putLong(“lastAccess”, System.currentTimeMillis());
editor.putString(“textEntryValue”, “Not Empty”);

// Salva le modifiche
editor.commit();
}

[/java]

Quindi innanzitutto si possono avere più sharedPreference per una applicazione e come si vede dal metodo factory

getSharedPreferences(...)

che prende in input il nome della sharedPreferece (sarebbe il nome del file dove verranno memorizzate) e le modalità di accesso.
La modalità di accesso possono essere:

  • MODE_PRIVATE
  • MODE_WORLD_READABLE
  • MODE_WORLD_WRITABLE

dove chiaramente la prima rende le preferences accedibili sono dall’applicazione che le ha create, mentre le altre due ne consentono la lettura o la scrittura anche ad altre applicazioni.

Una volta ottenuta l’istanza della SharedReference è possibile accedere ai dati in essa memorizzati attraverso una serie di metodi getter messi a disposizione per tipo.

  • getBoolean
  • getFloat
  • getInt
  • getLong
  • getString

la classe è un singleton a livello di applicazione e quindi ne esiste in memria sempre e sola una istanza. Grazie a questo è possibile anche registrarsi come listerner di eventi nel caso in cui vengano aggiunte o rimosse chiavi semplicemente fornendo un oggetto che implementi l’interfaccia SharedPreferences.OnSharedPreferenceChangeListener

ma per questo vi rimando alla documentazione ufficiale che è molto ben fatta.

Preferences di configurazione

Questo è l’uso più frequente che si fa delle Preferences ed utilizza, oltre alla classe SharedPreferences anche l’infrastruttura messa a  disposizione da Android per la creazione delle maschere di “Settings”

L’idea alla base è la seguente: Si immagini di dover dotare l’applicazione di una finestra di configurazione in cui indicare uno o più parametri (magari raggruppati logicamente) in modo che l’utente cambi i propri dati e memorizzare i dati da qualche parte (database). Successivamente si deve poter accedere in lettura a tali dati per adattare l’applicazione alle varie esigenze.  Se si dovesse fare tutto a mano, lo sviluppatore dovrebbe creare una nuova activity e programmare tutti i dati a mano, accedere al database, ecc, ecc.

Con questo framework è possibile fare tutto disgnando in XML la form di preference (come si fa per le activity) e demandare il tutto ad Android. Vediamo un esempio.

Supponiamo che l’applicazione che stiamo sviluppando debba dare possibilità all’utente di configurare dei parametri in cui bisogna:

  • Indicare se effettuare o meno il login ad un server
  • In caso affermativo, indicare la login e la password
  • Scegliere un server tra una lista predefinita.

Una volta fatte queste configurazioni, queste devono essere salvate e recuperate in seguito.

Per fare questo è necessario definire:

  • Un file XML che rappresenta la form delle preferences
  • Una activity che la gestisce (molto semplice)
  • Scrivere il codice per leggere i valori

Il file XML per la gestione delle preference potrebbe essere questo:

[xml]

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">

<PreferenceCategory  android:title="Settings …">

<CheckBoxPreference android:key="flagCheckLogin"    android:title="Accedere al Server"

android:summaryOn="L’accesso verrà effettuato" android:defaultValue="false"

android:summaryOff="Non si farà accesso al server"  />

</PreferenceCategory>

<!– Server section –>

<PreferenceCategory  android:title="Server Settings">

<EditTextPreference android:key="textLogin" android:title="Login"

android:summary="Indicare la login del server" android:defaultValue=""

android:dependency="flagCheckLogin"     />

<EditTextPreference android:key="textPassword" android:title="Password"

android:summary="Indicare la password di accesso" android:defaultValue=""

android:dependency="flagCheckLogin"    />

<ListPreference android:key="tipoLogin" android:title="Tipo di login"

android:summary="Selezionare il tipo di login"

android:entries="@array/loginEntries" android:entryValues="@array/loginEntriesValues"

android:dependency="flagCheckLogin" />

</PreferenceCategory>

</PreferenceScreen>

[/xml]

Dove sono state create due sezioni (solo per un effetto grafico) la prima contenente la check-box per indicare se effettuare o meno il login, mentre nella seconda ci sono i campi per il login, la password e la combo con la selezione del server (con dei valori predefiniti)
Per complicare un po’ la cosa, tutti i campi di input della seconda sezione “dipendono dal” valore del flag del primo campo. Questo fa si che se non si seleziona il flag non sarà possibile valorizzare i campi successivi. Graficamente si presenta così:

da notare che tutto (componenti e testo) è stato definito tramite file XML ed anche il testo da visualizzare nel caso in cui il flag è selezionato (attributo summaryOn) o meno (summaryoff).

Il framework fa si che ogni volta che i valori vengono modificati i dati vengono salvati automaticamente sulle SharedPrefereces dell’applicazione (anche se shared non sono) con la chiave indicata. Nel caso di campi di input (login e password) e di liste (i cui valori sono definiti in un altro file xml per semplicità ma potrebbero essere ricavati a run time), l’aspetto è il seguente:

Quindi, in tutto e per tutto simile alle configurazioni che si hanno di base nell’ambiente android.
Tornando al codice, quello che c’è da scrivere è veramente poco. Per invocare l’activity è necessario (da una activity o in seguito al click su un menù) avere il seguente codice:

[java]

public void showSettings(View v) {

Intent intent = new Intent(this, MyPreferencesActivity.class);

startActivity(intent);

}

[/java]

(quindi una semplice invocazione di una activity), mentre il codice dell’activity MyPreferencesActivity è il seguente:

[java]

public class MyPreferencesActivity extends PreferenceActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

addPreferencesFromResource(R.xml.preferences);

}

[/java]

anche qui niente di difficile. Le uniche due cose da fare sono:

  • Estendere dalla classe PreferenceActivity – che a sua volta è una actiivity e che ha tutto il codice necessario alla gestione dei vari componenti.
  • Aggiungere il layout delle preferences tramite il metodo addPreferencesFromResource ed indicare l’ ID del file XML che definisce il layout dell’activity

Conclusioni

L’ultimo passo da fare è quello, all’interno della nostra applicazioni, accedere ai parameti di configurazione. Questo si fa semplicemente con il metodo :

SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(this);

ed una volta ottenuta la sharedPreference, richiedere tutti i dati di cui si ha bisogno attraverso i metodi getter.
Con questo framework e con veramente poco codice è possibile creare form (activity) di configurazione velocemente ed in maniera standard.

Buon divertimento …

Comments
To Top