--
Stockage Android et processus en Background

CHAPITRE
N°4
1. Les options de Stockage Android
Android présente différents options pour la persistance des données. La solution choisie dépend des besoins spécifiques de l’application : données privées ou partagées, l’espace de stockage nécessaire pour ces données.
Les options de stockages sont les suivantes :
* Shared Preferences : cette technique stocke les données de l’application sous forme clé/valeur. * Stockage interne : les données stockées dans la mémoire du téléphone en utilisant les fichiers I/O. * SQLite Databases : stocke les données dans une base locale SQLIite. * Base de données externe : On utilise les web services pour se connecter aux données stockés sur un serveur de BD externe. * ContentProviders pour partager les données entre les applications.
1. Les fichiers I/O
Il s’agit de lire et écrire dans un fichier comme en java. Tous les appareils Android disposent de deux zones de stockage de fichiers: le stockage «interne» et le stockage «externe».
| | | | --- | --- | | **Stockage interne** | **Stockage externe** | | * C'est toujours disponible. * Les fichiers enregistrés ici sont accessibles uniquement par votre application. * Lorsque l'utilisateur désinstalle votre application, le système supprime tous les fichiers de votre application du stockage interne. * Le stockage interne est le meilleur lorsque vous voulez être sûr que ni l'utilisateur ni les autres applications ne peuvent accéder à vos fichiers. | * Il n'est pas toujours disponible, car l'utilisateur peut le retirer du périphérique. * Lorsque l'utilisateur désinstalle votre application, le système supprime les fichiers de votre application uniquement si vous les enregistrez dans le répertoire de getExternalFilesDir (). * Le stockage externe est le meilleur emplacement pour les fichiers ne nécessitant pas de restrictions d'accès et pour les fichiers partagés avec d'autres applications. |
* Permissions
Pour écrire sur le stockage externe, vous devez demander l'autorisation WRITE\_EXTERNAL\_STORAGE dans votre fichier manifeste
<manifest ...> <uses-permission android:name="android.permission.WRITE\_EXTERNAL\_STORAGE" /> ... </manifest>
Prochainement dans les releases futurs, il faut avoir la permission de lire, il faut donc ajouter
<manifest ...> <uses-permission android:name="android.permission.READ\_EXTERNAL\_STORAGE" /> ... </manifest>
1. Enregistrer dans la mémoire interne
String filename = "myfile"; String string = "Hello world!"; FileOutputStream outputStream;
try { outputStream = openFileOutput(filename, Context.MODE\_PRIVATE); outputStream.write(string.getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); }
1. Lire de la mémoire interne
public void LectureInterne(View v) { String FILENAME="monfichier.txt"; FileInputStream fis; byte[] buffer=new byte[1024]; StringBuilder content =new StringBuilder(); try{ fis=openFileInput(FILENAME); while(fis.read(buffer)!=-1) {content.append(new String(buffer));} }catch(FileNotFoundException e){e.printStackTrace();} catch(IOException e){e.printStackTrace();}
AlertDialog.Builder alert=new AlertDialog.Builder(MainActivity.this) .setMessage(content.toString()) .setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } });
alert.show();
1. Enregister dans la mémoire externe
final String storageState= Environment.**getExternalStorageState()**; if(storageState.equals(Environment.MEDIA\_MOUNTED)) {//on peut lire et écrire
File outFile=getExternalFilesDir(null); String Filename="/monfichier.txt"; String str="exemple de stockage externe";
try{
BufferedWriter bw = new BufferedWriter(new FileWriter(outFile+Filename)); bw.write(str); bw.close(); } catch(FileNotFoundException e){e.printStackTrace();} catch(IOException e){e.printStackTrace();}
}
* il est nécessaire de vérifier la disponibilité du stockage externe avant d’effectuer n’importe quelle requête en utilisant la méthode getExternalStorageState.
Les différents états possibles :
* MEDIA\_BAD\_REMOVAL : le stockage est débranché avant d’être démonté. * MEDIA\_CHACKING : le stockage est en cours de vérification. * MEDIA\_MOUNTED : le stockage est disponible en lecture/écriture * MEDIA\_MOUNTED\_READ\_ONLY : le stockage est disponible en lecture seule * MEDIA\_NOFS : le stockage est disponible mais le formatage n’est pas pris en charge. * MEDIA\_REMOVED : le stockage n’est pas présent. * MEDIA\_SHARED : le stockage est utilisé en USB (connecté au PC). * MEDIA\_UNMOUNTABLE : le stockage est présent mais ne peut pas être monté. * MEDIA\_UNMOUNTED : le stockage est présent mais n’est pas monté.
1. Lecture d’un fichier externe
// accéder au répertoire par défaut du support externe
File outFile = Environment.getExternalStorageDirectory();
// Accès au fichier
File file = new File(outFile + "/monfichier.txt");
// Tester si le fichier désiré existe, sinon, un Toast affiche un message d’erreur
if (!file.exists()) {
Toast.makeText(this, "Fichier n’existe pas dans la carte SD", 1000).show();
Publicité
}else{ // Si le fichier existe
try {
// Créer un BufferedReader pour parcourir le fichier en lecture
BufferedReader br = new BufferedReader(new FileReader(file));
// Stocker le contenu du fichier dans un buffer
String line;
StringBuffer buffer = new StringBuffer();
while ((line = br.readLine()) != null) {
buffer.append(line);
}
// Fermer le fichier
br.close();
} catch(FileNotFoundException e){e.printStackTrace();} catch(IOException e){e.printStackTrace();} }
1. Shared Preferences
Si vous avez une collection relativement petite de valeurs-clés que vous souhaitez enregistrer, utilisez les API SharedPreferences. Un objet SharedPreferences pointe vers un fichier contenant des paires clé-valeur et fournit des méthodes simples pour les lire et les écrire. Chaque fichier SharedPreferences est géré par le framework et peut être privé ou partagé.
1. Instancier Shared Preferences
Vous pouvez créer un nouveau fichier de préférences partagées ou accéder à un fichier existant en appelant l'une des deux méthodes suivantes:
* **getSharedPreferences ()** - Utilisez-le si vous avez besoin de plusieurs fichiers de préférences partagées identifiés par leur nom, que vous spécifiez avec le premier paramètre. Vous pouvez l'appeler depuis n'importe quel contexte de votre application. * **getPreferences ()** - Utilisez-le à partir d'une activité si vous devez utiliser un seul fichier de préférences partagées pour l'activité. Comme ce fichier récupère un fichier de préférences partagées par défaut appartenant à l'activité, il n'est pas nécessaire de fournir un nom.
Par exemple, le code suivant est exécuté à l'intérieur d'un fragment. Il accède au fichier de préférences partagées identifié par la chaîne de ressources R.string.preference\_file\_key et l'ouvre en mode privé afin que le fichier soit accessible uniquement par votre application.
Contexte contexte = getActivity ();
SharedPreferences sharedPref = context.getSharedPreferences (
getString (R.string.preference\_file\_key), Context.MODE\_PRIVATE);
Lorsque vous nommez vos fichiers de préférences partagées, vous devez utiliser un nom qui est uniquement identifiable à votre application, par exemple "com.example.myapp.PREFERENCE\_FILE\_KEY"
Alternativement, si vous avez besoin d'un seul fichier de préférences partagées pour votre activité, vous pouvez utiliser la méthode getPreferences ():
SharedPreferences sharedPref = getActivity (). GetPreferences(Context.MODE\_PRIVATE);
N.B: si vous créez un fichier de préférences partagées avec **MODE\_WORLD\_READABLE** ou **MODE\_WORLD\_WRITEABLE**, toutes les autres applications connaissant l'identificateur de fichier peuvent accéder à vos données.
1. Ecrire dans shared preferences
Pour écrire dans un fichier de préférences partagé, créez un **SharedPreferences.Editor** en appelant edit () sur vos préférences SharedPreferences.
Passez les clés et les valeurs que vous voulez écrire avec des méthodes telles que
**putInt ()** et **putString ()**. Appelez ensuite **commit()** pour enregistrer les modifications. Par exemple:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE\_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(getString(R.string.saved\_high\_score), newHighScore); editor.commit();
1. Lire à partir de shared preferences
Pour extraire les valeurs d'un fichier de préférences partagées, appelez des méthodes telles que getInt () et getString (), fournissant la clé de la valeur que vous voulez et éventuellement une valeur par défaut à retourner si la clé n'est pas présente. Par exemple:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE\_PRIVATE); int defaultValue = getResources().getInteger(R.string.saved\_high\_score\_default); long highScore = sharedPref.getInt(getString(R.string.saved\_high\_score), defaultValue);
1. Sauvagarder l’état d’une actvité
SharedPreferences est utilisé pour sauvegarder l’état d’une activité.
Par exemple : sauvegarder les champs d’un formulaire lors d’une interruption causée par un appel téléphonique. Lorsque l’utilisateur reprend l’activité, on charge de nouveau le formulaire.
Il faut reimplémenter onPause() pour sauvegarder les préférences et onResume() pour retrouver les préférences.
1. Processus en Background
Par défaut, le code d'application s'exécute dans le thread principal. Chaque déclaration est donc exécutée dans l'ordre. Si vous effectuez une opération de longue durée, l'application se bloque jusqu'à ce que l'opération correspondante soit terminée.
Pour offrir une bonne expérience utilisateur, toutes les opérations potentiellement lentes d'une application Android doivent être exécutées de manière asynchrone. Cela peut être archivé via des constructions concurrentielles du langage Java ou du framework Android. Les opérations potentiellement lentes sont par exemple l'accès au réseau, aux fichiers et aux bases de données et les calculs complexes.
Publicité
Le background processing en Android se fait à travers :
* Les threads * Les services * Les fragments en background
1. Les threads
On a 2 types de threads :
* UI thread : main thread. C’est le thread de l’activité crée lors de la création de l’activité. * Worker thread : chaque thread lancé par UI thread pour assurer une tâche en background.
Pour créer des workers thread, on utilise java thread ou la solution android simplifiée : AsyncTask.
AsyncTask : c’est une classe ayant les propriétés qui suivent :
* Permet le traitement asynchrone dans votre application * Crée un worker thread et envoie les résultats au UI thread * Idéal pour les tâches courtes en background
Exemple : Se connecter à internet pour collecter des données nécessite de lancer un AsyncTask
AsyncTask doit être sous-classifié pour être utilisé. La sous-classe remplacera au moins une méthode (**doInBackground** (Params ...)), et remplacera le plus souvent une seconde (**onPostExecute** (Result)).
Voici un exemple: telecharger des fichiers en background
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>
* URL:Paramètres envoyés au thread pour l’exécution * Long : résultat final * Integer : pourcentage de progression du thread durant l’exécution
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0;
for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) \* 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; }
protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); }
protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
une fois crée, l’AsyncTask est execute simplement:
new DownloadFilesTask().execute(url1, url2, url3);
1. Les services
Un service est un composant d'application qui peut effectuer des opérations de longue durée en arrière-plan et ne fournit pas d'interface utilisateur. Un autre composant d'application peut démarrer un service et il continue à s'exécuter en arrière-plan même si l'utilisateur passe à une autre application. En outre, un composant peut se lier à un service pour interagir avec lui et même effectuer une communication interprocessus (IPC). Par exemple, un service peut gérer des transactions réseau, jouer de la musique, effectuer des E / S de fichiers ou interagir avec un fournisseur de contenu, tous en arrière-plan.
Un service hérite de deux classes : **Service te IntentService**
* Exemple de lancement d’un service :
Le lancement d’une application musicale se fait comme suit :
* Démarrage de l’activité qui permet de faire le choix de chanson * L’utilisateur lance la musique * Le service diffuse cette musique * L’utilisateur peut quitter l’application en fermant l’activité * La musique continue à être diffusée * Etapes de communication avec un service :
Pour communiquer avec un service il faut :
* S’y connecter (il se lance si il était arreté) * Utiliser l’interface que présenter ce service, exemple : Play(), Pause(), next() ... * Un service s’exécute dans un Thread et donc ne bloque pas le reste du terminal quand il tourne en fond. * Implémentation d’un service
Le service se déclare dans le Manifest dans le tag *application*:
**<service** android:name=".AndroService"**/>**
La structure d'une classe de service ressemble à une activité. Pour réaliser un service, on hérite de la classe **Service** et on implémente les méthodes de création/démarrage/arrêt du service. La nouvelle méthode qu'il faut implémenter est **onBind** qui permet aux IPC (InterProcessCommunication) de faire des appels à des méthodes distantes.
public class AndroService extends Service {
public void onCreate() {
// Création du service
}
public void onDestroy() {
// Destruction du service
}
@deprecated // utile pour les versions antérieures d'Android 2.0
public void onStart(Intent intent, int startId) {
// Démarrage du service
Publicité
}
public int onStartCommand(Intent intent, int flags, int startId) {
// Démarrage du service
return START\_STICKY;
}
public IBinder onBind(Intent arg0) {
return null;
}}
* + Démarrer / Arrêter un service
Les *Intents* fournissent un moyen de démarrer/arrêter un service en utilisant le nom de la classe du service ou un identifiant d'action:
startService(new Intent(this, AndroService.class));
stopService(new Intent(this, AndroService.class));
startService(new Intent("iset.sem.manageServiceAction"));
Ces méthodes sont à invoquer dans l'activité de l'application développée. On dit alors que le service est *local* à l'application: il va même s'exécuter dans le *thread* de l'application. Un bouton de démarrage d'un service local est simple à coder:
Button b = (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent startService = new Intent("iset.dsi.manageServiceAction");
startService(startService);}});
1. IntentService
Un autre *design pattern* a été simplifié dans Android: l'exécution dans un processus isolé de *jobs* provenant d'une autre partie de l'application. Les *jobs* sont provisionnés au travers d'un **Intent** et l'**IntentService** exécute la tâche dans la méthode **onHandleIntent()**, indépendamment de l'*UI thread*.
public class MonServiceLourd extends IntentService {
{public MonServiceLourd() {
super("MonServiceLourd");}
@Override
protected void onHandleIntent(Intent intent) {
... // Tâche longue, par exemple un téléchargement}}
Le bénéfice, qui n'est pas visible ici, est que les **Intent** sont mis dans une file d'attente et traités les uns après les autres, évitant une surcharge du système. Chaque **Intent** est traité par **onHandleIntent()** et le service s'arrête tout seul quand il n'y a plus rien à traiter.
1. Se connecter à Internet via Android
La connexion se fait à travers un AsynchTask comme suit :
**class** connexiontask **extends** AsyncTask<String,Void,String> { @Override **protected** String doInBackground(String... strings) { String res = **null**; res= connectweb(); **return** res; }
@Override **protected void** onPostExecute(String res) { **if**(res!=**null**) **txt**.setText(res); **super**.onPostExecute(res);
} }
**public** String connectweb() { String responseText=**null**; URL url= **null**; **try** { url = **new** URL(**"http://api.geonames.org/postalCodeLookupJSON?postalcode=6600&country=AT&username=demo"**); } **catch** (MalformedURLException e) { e.printStackTrace(); } **try** { HttpURLConnection httpURLConnection= (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod(**"Get"**); httpURLConnection.connect(); **int** httpResponseCode = httpURLConnection.getResponseCode();
BufferedReader bufferedReader=**new** BufferedReader(**new** InputStreamReader(httpURLConnection.getInputStream())); String inputLine; StringBuffer stringBuffer=**new** StringBuffer(); **while**((inputLine=bufferedReader.readLine())!=**null**) {stringBuffer.append(inputLine);
} bufferedReader.close(); responseText=stringBuffer.toString(); } **catch** (IOException e) { e.printStackTrace(); } **return** responseText; }
