web-dev-qa-db-fra.com

Méthode universelle pour écrire sur une carte SD externe sur Android

Dans mon application, je dois stocker beaucoup d'images dans la mémoire de stockage de l'appareil. De tels fichiers tendent à occuper la mémoire de l'appareil et je veux permettre aux utilisateurs de choisir une carte SD externe comme dossier de destination.

Je lis partout que Android ne permet pas aux utilisateurs d'écrire sur une carte SD externe, par carte SD, j'entends la carte SD externe et montable et pas le stockage externe, mais le gestionnaire de fichiers les applications parviennent à écrire sur la carte SD externe sur toutes les versions Android.

Quel est le meilleur moyen d’accorder un accès en lecture/écriture à une carte SD externe à différents niveaux d’API (Pre-KitKat, KitKat, Lollipop +)?

Mise à jour 1

J'ai essayé la méthode 1 de la réponse de Doomknight, sans succès: Comme vous pouvez le constater, je vérifie les autorisations au moment de l'exécution avant de tenter d'écrire sur SD:

HashSet<String> extDirs = getStorageDirectories();
for(String dir: extDirs) {
    Log.e("SD",dir);
    File f = new File(new File(dir),"TEST.TXT");
    try {
        if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) {
            f.createNewFile();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Mais j'obtiens une erreur d'accès, essayée sur deux appareils différents: HTC10 et Shield K1.

10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F8
10-22 14:52:57.329 30280-30280/? W/System.err: Java.io.IOException: open failed: EACCES (Permission denied)
10-22 14:52:57.329 30280-30280/? W/System.err:     at Java.io.File.createNewFile(File.Java:939)
10-22 14:52:57.329 30280-30280/? W/System.err:     at com.myapp.activities.TestActivity.onResume(TestActivity.Java:167)
10-22 14:52:57.329 30280-30280/? W/System.err:     at Android.app.Instrumentation.callActivityOnResume(Instrumentation.Java:1326)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.Activity.performResume(Activity.Java:6338)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread.performResumeActivity(ActivityThread.Java:3336)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread.handleResumeActivity(ActivityThread.Java:3384)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2574)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread.access$900(ActivityThread.Java:150)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1399)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.os.Handler.dispatchMessage(Handler.Java:102)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.os.Looper.loop(Looper.Java:168)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Android.app.ActivityThread.main(ActivityThread.Java:5885)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Java.lang.reflect.Method.invoke(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err:     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:819)
10-22 14:52:57.330 30280-30280/? W/System.err:     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:709)
10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: Android.system.ErrnoException: open failed: EACCES (Permission denied)
10-22 14:52:57.330 30280-30280/? W/System.err:     at libcore.io.Posix.open(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.Java:186)
10-22 14:52:57.330 30280-30280/? W/System.err:     at Java.io.File.createNewFile(File.Java:932)
10-22 14:52:57.330 30280-30280/? W/System.err:  ... 14 more
67
Vektor88

Résumé

Vous pouvez accorder un accès en lecture/écriture à une carte SD externe sur différents niveaux d'api ( API23 + au moment de l'exécution ).

Depuis KitKat, les autorisations sont non nécessaire si vous utilisez des répertoires spécifiques à l'application, requis sinon.

Voie universelle:

Le historique dit qu'il n'y a pas moyen d'écrire sur la carte SD externe mais continue ...

Ce fait est présenté par ces exemples de configurations de stockage externe pour les périphériques .

Méthode basée sur l'API:

Avant KitKat essayez d'utiliser méthode Doomsknight 1, méthode 2 sinon.

Demander des autorisations dans le manifeste (Api <23) et au moment de l'exécution (Api> = 23).

Manière recommandée:

ContextCompat.getExternalFilesDirs résout l'erreur d'accès lorsque vous n'avez pas besoin de partager des fichiers.

La méthode de partage sécurisée consiste à utiliser fournisseur de conten ou nouveau Storage Access Framework .

Méthode respectueuse de la vie privée:

À partir de Android Q Bêta 4 , les applications qui ciblent Android 9 (API de niveau 28) ou inférieur ne voient aucune modification, par défaut.

Les applications ciblant Android Q par défaut (ou optant pour cette option) reçoivent un vue filtrée dans le stockage externe.


1. Réponse initiale.

Manière universelle d'écrire sur une carte SD externe sur Android

Il n'y a pas de manière universelle sur écrire sur la carte SD externe sous Android en raison de modifications continues :

  • Pré-KitKat: la plate-forme officielle Android ne prend pas en charge les cartes SD, sauf exception.

  • KitKat: introduction d'API permettant aux applications d'accéder aux fichiers situés dans des répertoires spécifiques à une application sur des cartes SD.

  • Lollipop: ajout d'API pour permettre aux applications de demander l'accès à des dossiers appartenant à d'autres fournisseurs.

  • Nougat: fourni une API simplifiée pour accéder aux répertoires de stockage externes communs.

  • ... Changement de confidentialité pour Android Q: stockage orienté vers les applications et les supports

Quel est le meilleur moyen d’accorder un accès en lecture/écriture à une carte SD externe à différents niveaux d’API?

Basé sur réponse de Doomsknight et le mien , et Dave Smith et messages du blog de Mark Murphy: 1 , 2 , :


2. Réponse mise à jour.

Mise à jour 1 . J'ai essayé la méthode 1 de la réponse de Doomknight, sans succès:

Comme vous pouvez le constater, je vérifie les autorisations au moment de l'exécution avant de tenter d'écrire sur SD ...

J'utiliserais des répertoires spécifiques à l'application pour éviter le problème de votre question mise à jour et ContextCompat.getExternalFilesDirs() à l'aide de documentation de getExternalFilesDir .

Améliorez l'heuristique pour déterminer ce qui représente un support amovible en fonction des différents niveaux de l'API tels que _Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.KitKat_

... Mais j'obtiens une erreur d'accès, essayée sur deux appareils différents: HTC10 et Shield K1.

N'oubliez pas que Android 6.0 prend en charge les périphériques de stockage portables et que les applications tierces doivent passer par Storage Access Framework . Vos appareils HTC1 et Shield K1 sont probablement les API 23.

Votre journal indique ne exception d’autorisation refusée accédant à _/mnt/media_rw_, comme ce correctif pour l’API 19+:

_<permission name="Android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" /> // this line is added via root in the link to fix it.
</permission>
_

Je n'ai jamais essayé, je ne peux donc pas partager de code, mais j'éviterais que for essaye d'écrire sur tous les répertoires retournés et cherche le meilleur répertoire de stockage disponible dans lequel écrire en fonction de l'espace restant .

Peut-être alternative de Gizm à votre méthode getStorageDirectories() c'est un bon point de départ.

_ContextCompat.getExternalFilesDirs_ résout le problème si vous n'en avez pas besoin accès à d'autres dossiers .


3. Android 1.0 .. Pré-KitKat.

Avant KitKat, essayez d'utiliser méthode Doomsknight 1 ou lisez cette réponse par Gnathonic.

_public static HashSet<String> getExternalMounts() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase(Locale.US).contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase(Locale.US).contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}
_

Ajoutez le code suivant à votre _AndroidManifest.xml_ et lisez Accès au stockage externe

L'accès au stockage externe est protégé par diverses autorisations Android.

À partir de Android 1.0, l'accès en écriture est protégé par la permission _WRITE_EXTERNAL_STORAGE_) .

À partir de Android 4.1, l'accès en lecture est protégé avec l'autorisation _READ_EXTERNAL_STORAGE_.

Pour ... écrire des fichiers sur le stockage externe, votre application doit acquérir ... autorisations système:

_<manifest ...>
    <uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> 
_

Si vous avez besoin des deux ..., vous devez demander uniquement l'autorisation _WRITE_EXTERNAL_STORAGE_.

Lire explication de Mark Murphy et recommandéDianne Hackborn et Dave Smith posts

  • Jusqu'au Android 4.4, il n'existait aucun support officiel pour les supports amovibles sous Android. À partir de KitKat, le concept de stockage externe "primaire" et "secondaire" apparaît dans l'API FMW.
  • Les applications précédentes ne font que s'appuyer sur l'indexation MediaStore, sont livrées avec le matériel ou examinent les points de montage et appliquent des méthodes heuristiques pour déterminer ce qui représente un support amovible.

4. Android 4.4 KitKat introduit le Storage Access Framework (SAF) .

Ignore la prochaine note à cause de bugs, mais essayez d'utiliser ContextCompat.getExternalFilesDirs():

  • Depuis Android 4.2, Google a demandé aux fabricants d’appareils de verrouiller les supports amovibles à des fins de sécurité (prise en charge de plusieurs utilisateurs) et de nouveaux tests ont été ajoutés dans la version 4.4.
  • Depuis KitKat getExternalFilesDirs() et d'autres méthodes ont été ajoutées pour renvoyer un chemin utilisable sur tous les volumes de stockage disponibles (le premier élément renvoyé est le volume principal).
  • Le tableau ci-dessous indique ce qu'un développeur peut essayer de faire et comment KitKat va réagir: enter image description here

Remarque: À partir de Android 4.4, ces autorisations ne sont pas nécessaires si vous ne lisez ou n'écrivez que des fichiers privés. app. Pour plus d'informations ..., voir enregistrement des fichiers privés de l'application .

_<manifest ...>
    <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"
                     Android:maxSdkVersion="18" />
</manifest>
_

Lisez également l'explication de Paolo Rovelli et essayez d'utiliser la solution de Jeff Sharkey depuis KitKat:

Dans KitKat, il existe désormais une API publique permettant d'interagir avec ces périphériques de stockage partagés secondaires.

Les nouvelles méthodes Context.getExternalFilesDirs() et Context.getExternalCacheDirs() peuvent renvoyer plusieurs chemins, y compris des périphériques principaux et secondaires.

Vous pouvez ensuite les parcourir et vérifier Environment.getStorageState() et File.getFreeSpace() pour déterminer le meilleur emplacement pour stocker vos fichiers.

Ces méthodes sont également disponibles sur ContextCompat dans la bibliothèque support-v4.

À partir de Android 4.4, le propriétaire, le groupe et les modes de fichiers sur des périphériques de stockage externes sont maintenant synthétisés en fonction de la structure de répertoires. Cela permet aux applications de gérer leurs répertoires spécifiques à un package sur un stockage externe sans avoir besoin de détenir l'autorisation large _WRITE_EXTERNAL_STORAGE_. Par exemple, l'application avec le nom de package _com.example.foo_ peut désormais accéder librement à _Android/data/com.example.foo/_ sur des périphériques de stockage externes sans autorisation. Ces autorisations synthétisées sont obtenues en encapsulant des périphériques de stockage bruts dans un démon Fuse.

Avec KitKat, vos chances d'obtenir une "solution complète" sans enracinement sont pratiquement nulles:

Le projet Android a définitivement échoué ici. Aucune application n'a un accès complet aux cartes SD externes:

  • gestionnaires de fichiers: vous ne pouvez pas les utiliser pour gérer votre carte SD externe. Dans la plupart des domaines, ils peuvent seulement lire mais pas écrire.
  • applications multimédia: vous ne pouvez plus modifier/réorganiser votre collection multimédia, car ces applications ne peuvent pas y écrire.
  • applications bureautiques: à peu près les mêmes

Le seul endroit 3rd les applications de parti sont autorisées à écrire sur votre carte externe sont "leurs propres répertoires" (c'est-à-dire _/sdcard/Android/data/<package_name_of_the_app>_).

Les seuls moyens de vraiment résoudre ce problème nécessitent soit le fabricant (certains l’ont corrigé, par exemple Huawei avec leur mise à jour KitKat pour le P6) - ou racine ... (l’explication d’Izzy continue ici)


5. Android 5.0 a introduit les modifications et la classe d'assistance DocumentFile .

getStorageState Ajouté dans l'API 19, obsolète dans l'API 21, tilisez getExternalStorageState(File)

Voici un excellent tutoriel pour interagir avec Storage Access Framework dans KitKat.

Interaction avec les nouvelles API de Lollipop est très similaire (explication de Jeff Sharkey) .


6. Android 6.0 Marshmallow introduit un nouveau modèle autorisations d'exécution .

Autorisations de demande au moment de l'exécution si l'API de niveau 23+ et lu Demandes d'autorisations au moment de l'exécution

À partir de Android 6.0 (API de niveau 23), les utilisateurs accordent des autorisations aux applications lorsque l'application est en cours d'exécution, pas lorsqu'ils installent l'application ... ou mettent à jour l'application ... l'utilisateur peut révoquer les autorisations.

_// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_EXTERNAL_STORAGE);
_

Android 6.0 introduit un nouveau modèle autorisations d'exécution où les applications demandent des fonctionnalités lorsque cela est nécessaire au moment de l'exécution. Etant donné que le nouveau modèle inclut les autorisations _READ/WRITE_EXTERNAL_STORAGE_, la plate-forme doit accorder de manière dynamique un accès au stockage sans supprimer ni redémarrer les applications déjà en cours d'exécution. Pour cela, il conserve trois vues distinctes de tous les périphériques de stockage montés:

  • / mnt/runtime/default est affiché pour les applications sans autorisations de stockage spéciales ...
  • / mnt/runtime/read est affiché pour les applications avec READ_EXTERNAL_STORAGE
  • / mnt/runtime/write est présenté aux applications avec WRITE_EXTERNAL_STORAGE

7. Android 7.0 fournit une API simplifiée pour accéder aux répertoires de stockage externes.

Accès au répertoire ciblé Dans Android 7.0, les applications peuvent utiliser de nouvelles API pour demander l'accès à des répertoires spécifiques stockage externe , y compris des répertoires sur un support amovible, tel qu'une carte SD. ...

Pour plus d'informations, reportez-vous à la section Formation Scoped Directory Access .

Lisez les messages de Mark Murphy: Faites attention avec l'accès au répertoire avec une portée limitée . Il était obsolète dans Android Q :

Notez que l'accès au répertoire périmé ajouté dans la version 7.0 est obsolète dans Android Q.

Plus précisément, la méthode createAccessIntent() sur StorageVolume est obsolète.

Ils ont ajouté une createOpenDocumentTreeIntent() qui peut être utilisée à la place.


8. Android 8.0 Oreo .. Android Q Les modifications de la version bêta.

À partir de Android O , le Storage Access Framework permet fournisseurs de documents personnalisés de créer des descripteurs de fichiers pouvant être recherchés pour les fichiers résidant dans une source de données distante ...

Autorisations , avant Android O , si une application a demandé une autorisation au moment de l'exécution et que l'autorisation était accordée, le système a également attribué à l’application, de manière incorrecte, le reste des autorisations appartenant au même groupe d’autorisations et enregistrées dans le manifeste.

Pour les applications ciblant Android O , ce problème a été corrigé. L'application reçoit uniquement les autorisations qu'elle a explicitement demandées. Cependant, une fois que l'utilisateur a accordé une autorisation à l'application, toutes les demandes d'autorisations ultérieures de ce groupe d'autorisations sont automatiquement accordées.

Par exemple, _READ_EXTERNAL_STORAGE_ et _WRITE_EXTERNAL_STORAGE_...

Mise à jour: Une version bêta antérieure de Android Q a temporairement remplacé les autorisations _READ_EXTERNAL_STORAGE_ et _WRITE_EXTERNAL_STORAGE_ par des informations plus précises. des autorisations spécifiques au support.

Remarque: Google a introduit rôles sur la version bêta 1 et les a supprimés de la documentation avant la version bêta 2 ...

Remarque: Les autorisations spécifiques aux collections de supports introduites dans les versions bêta précédentes —_READ_MEDIA_IMAGES_, _READ_MEDIA_AUDIO_ et _READ_MEDIA_VIDEO_— sont maintenant obsolètes . Plus d'infos:

Examen de la bêta 4 (API finales) par Mark Murphy: La mort du stockage externe: la fin de la saga (?)

"La mort est plus universelle que la vie. Tout le monde meurt, mais tout le monde ne vit pas." - Andrew Sachs


9. Questions connexes et réponses recommandées.

Comment puis-je obtenir un chemin de carte SD externe pour Android 4.0 +?

mkdir () fonctionne dans la mémoire flash interne, mais pas sur la carte SD?

Diff entre getExternalFilesDir et getExternalStorageDirectory ()

Pourquoi getExternalFilesDirs () ne fonctionne pas sur certains périphériques?

Comment utiliser la nouvelle API d'accès à la carte SD présentée pour Android 5.0 (Lollipop)

Écriture sur une carte SD externe sous Android 5.0 et versions ultérieures

Autorisation d'écriture de la carte SD Android à l'aide de SAF (Storage Access Framework)

SAFFAQ: FAQ sur le Storage Access Framework


10. Bogues et problèmes associés.

Bug: sur Android 6, l'utilisation de getExternalFilesDirs ne vous permet pas de créer de nouveaux fichiers dans ses résultats

L'écriture dans le répertoire renvoyé par getExternalCacheDir () sur Lollipop échoue sans l'autorisation d'écriture

103
albodelu

Je crois qu'il existe deux méthodes pour y parvenir:

MÉTHODE 1: (ne () _ pas _ fonctionne sur 6.0 et au-dessus, en raison de changements d'autorisation)

J'utilise cette méthode depuis des années sur de nombreuses versions d'appareils sans aucun problème. Le crédit est dû à la source d'origine, car ce n'est pas moi qui l'ai écrit.

Il retournera tous les médias montés (y compris les cartes SD réelles) dans une liste d'emplacements de répertoires de chaînes. Avec la liste, vous pouvez ensuite demander à l'utilisateur où enregistrer, etc.

Vous pouvez l'appeler comme suit:

 HashSet<String> extDirs = getStorageDirectories();

Méthode:

/**
 * Returns all the possible SDCard directories
 */
public static HashSet<String> getStorageDirectories() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase().contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase().contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}

MÉTHODE 2:

Utiliser la bibliothèque de support v4

import Android.support.v4.content.ContextCompat;

Appelez simplement le suivant pour obtenir une liste de File emplacements de stockage.

 File[] list = ContextCompat.getExternalFilesDirs(myContext, null);

Les emplacements diffèrent cependant par leur utilisation.

Renvoie les chemins absolus vers les répertoires spécifiques à l'application sur tous les périphériques de stockage externes où l'application peut placer les fichiers persistants qu'elle possède. Ces fichiers sont internes à l'application et ne sont généralement pas visibles par l'utilisateur en tant que média.

Les périphériques de stockage externes renvoyés ici sont considérés comme faisant partie intégrante du périphérique, y compris les fentes de stockage externe émulées et de supports de stockage physiques, telles que les cartes SD insérées dans un compartiment à piles. Les chemins renvoyés n'incluent pas les périphériques transitoires, tels que les clés USB.

Une application peut stocker des données sur tout ou partie des périphériques retournés. Par exemple, une application peut choisir de stocker de gros fichiers sur le périphérique avec le plus grand espace disponible

Plus d'infos sur ContextCompat

Ils sont comme des fichiers spécifiques à une application. Caché d'autres applications.

9
Doomsknight

En plus de toutes les autres réponses de Nice, je pourrais ajouter un peu plus à cette question afin qu'elle puisse couvrir davantage les lecteurs. Dans ma réponse, j'utiliserais deux ressources comptables pour présenter le stockage externe.

La première ressource provient de Programmation Android, Guide du Big Nerd Ranch Guide, 2e édition , chapitre 16, page 294.

Le livre décrit les méthodes de base et externes de fichier et de répertoire. Je vais essayer de résumer ce qui pourrait être pertinent pour votre question.

La partie suivante du livre:

Stockage externe

Votre photo a besoin de plus qu'une place sur l'écran. Les images en taille réelle sont trop volumineuses pour rester dans une base de données SQLite, encore moins un Intent. Ils auront besoin d’un endroit pour vivre sur le système de fichiers de votre appareil. Normalement, vous les mettriez dans votre stockage privé. Rappelez-vous que vous avez utilisé votre stockage privé pour sauvegarder votre base de données SQLite. Avec des méthodes telles que Context.getFileStreamPath(String) et Context.getFilesDir(), vous pouvez également faire la même chose avec des fichiers normaux (qui vivront dans un sous-dossier adjacent au sous-dossier de base de données dans lequel votre base de données SQLite habite)

Méthodes de base de fichiers et de répertoires dans Contexte

| Method                                                                                |
|---------------------------------------------------------------------------------------|
|File getFilesDir()                                                                      |
| - Returns a handle to the directory for private application files.                    |
|                                                                                       |
|FileInputStream openFileInput(String name)                                             |
| - Opens an existing file for input (relative to the files directory).                 |
|                                                                                       |
|FileOutputStream openFileOutput(String name, int mode)                                 |
| - Opens a file for output, possibly creating it (relative to the files directory).    |
|                                                                                       |
|File getDir(String name, int mode)                                                     |
| - Gets (and possibly creates) a subdirectory within the files directory.              |
|                                                                                       |
|String[] fileList()                                                                    |
| - Gets a list of file names in the main files directory, such as for use with         |
|   openFileInput(String).                                                              |
|                                                                                       |
|File getCacheDir()                                                                     |
| - Returns a handle to a directory you can use specifically for storing cache files.   |
|   You should take care to keep this directory tidy and use as little space as possible|

Si vous stockez des fichiers que seule votre application actuelle doit utiliser, ces méthodes sont exactement ce dont vous avez besoin.

Par contre, si vous avez besoin d’une autre application pour écrire dans ces fichiers, vous n’aurez aucune chance: bien qu’il y ait un drapeau Context.MODE_WORLD_READABLE, vous pouvez le passer à openFileOutput(String, int), il est déconseillé, et pas complètement. fiable dans ses effets sur les nouveaux appareils. Si vous stockez des fichiers à partager avec d'autres applications ou recevez des fichiers d'autres applications (fichiers tels que des images stockées), vous devez plutôt les stocker sur un stockage externe.

Il existe deux types de stockage externe: principal et tout le reste. Tous les appareils Android ont au moins un emplacement pour le stockage externe: l'emplacement principal situé dans le dossier renvoyé par Environment.getExternalStorageDirectory(). Cela peut être une carte SD, mais de nos jours, elle est plus communément intégrée à l'appareil lui-même. Certains appareils peuvent avoir un stockage externe supplémentaire. Cela tomberait sous "tout le reste".

Context fournit également de nombreuses méthodes pour accéder à un stockage externe. Ces méthodes fournissent des moyens faciles d'accéder à votre stockage externe principal et des moyens assez faciles d'accès à tout le reste. Toutes ces méthodes stockent également les fichiers dans des emplacements accessibles au public, alors soyez prudent avec eux.

Méthodes de fichiers et de répertoires externes dans Contexte

| Method                                                                                |
| --------------------------------------------------------------------------------------|
|File getExternalCacheDir()                                                             |
| - Returns a handle to a cache folder in primary external storage. Treat it like you do|
|   getCacheDir(), except a little more carefully. Android is even less likely to clean |
|   up this folder than the private storage one.                                        |
|                                                                                       |
|File[] getExternalCacheDirs()                                                          |
| - Returns cache folders for multiple external storage locations.                      |
|                                                                                       |
|File getExternalFilesDir(String)                                                       |
| - Returns a handle to a folder on primary external storage in which to store regular  |
|   files. If you pass in a type String, you can access a specific subfolder dedicated  |
|   to a particular type of content. Type constants are defined in Environment, where   |
|   they are prefixed with DIRECTORY_.                                                  |
|   For example, pictures go in Environment.DIRECTORY_PICTURES.                         |
|                                                                                       |
|File[] getExternalFilesDirs(String)                                                    |
| - Same as getExternalFilesDir(String), but returns all possible file folders for the  |
|   given type.                                                                         |
|                                                                                       |
|File[] getExternalMediaDirs()                                                          |
| - Returns handles to all the external folders Android makes available for storing     |
|   media – pictures, movies, and music. What makes this different from calling         |
|   getExternalFilesDir(Environment.DIRECTORY_PICTURES) is that the media scanner       |
|   automatically scans this folder. The media scanner makes files available to         |
|   applications that play music, or browse movies and photos, so anything that you     |
|   put in a folder returned by getExternalMediaDirs() will automatically appear in     |
|   those apps.                                                                         |

Techniquement, les dossiers externes fournis ci-dessus peuvent ne pas être disponibles, car certains appareils utilisent une carte SD amovible pour le stockage externe. En pratique, cela pose rarement problème, car presque tous les appareils modernes disposent d’un stockage interne inamovible pour leur stockage "externe". Il ne faut donc pas aller très loin pour en rendre compte. Mais nous vous recommandons d’inclure un code simple pour vous protéger de cette possibilité, ce que vous ferez dans un instant.

Autorisation de stockage externe

En général, vous avez besoin d'une autorisation pour écrire ou lire à partir d'un stockage externe. Les autorisations sont des valeurs de chaîne connues que vous insérez dans votre manifeste à l'aide de la balise <uses-permission>. Ils disent à Android que vous voulez faire quelque chose pour lequel Android veut que vous leur demandiez l'autorisation.

Ici, Android s'attend à ce que vous demandiez une autorisation car il souhaite imposer certaines règles de responsabilité. Vous indiquez à Android que vous devez accéder à un stockage externe. Android indique ensuite à l'utilisateur qu'il s'agit là d'une des choses que votre application effectue lorsqu'elle tente de l'installer. De cette façon, personne n'est surpris lorsque vous commencez à enregistrer des éléments sur leur carte SD.

Dans Android 4.4, KitKat, ils ont assoupli cette restriction. Puisque Context.getExternalFilesDir(String) renvoie un dossier spécifique à votre application, il est donc logique que vous souhaitiez pouvoir lire et écrire des fichiers qui y résident. Ainsi, sur Android 4.4 (API 19) et les versions ultérieures, vous n'avez pas besoin de cette autorisation pour ce dossier. (Mais vous en avez toujours besoin pour d'autres types de stockage externe.)

Ajoutez une ligne à votre manifeste qui demande l’autorisation de lire le stockage externe, mais uniquement jusqu’à la liste d’API 16.5 Demande d’autorisation de stockage externe (AndroidManifest.xml)

<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
         package="com.bignerdranch.Android.criminalintent" >
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE"
         Android:maxSdkVersion="18" />

L'attribut maxSdkVersion fait en sorte que votre application ne demande cette autorisation que sur les versions de Android antérieures à l'API 19, Android KitKat. Notez que vous ne demandez que de lire le stockage externe. Il existe également une permission WRITE_EXTERNAL_STORAGE, mais vous n'en avez pas besoin. Vous n'écrirez rien sur une mémoire de stockage externe: l'application Appareil photo le fera pour vous.

La deuxième ressource est la suivante lien , mais vous pouvez également accéder à la section à l’aide de la section Stockage externe .

Référence:

Plus de lecture:

Clause de non-responsabilité: Cette information provient de Android Programmation: The Big Nerd Ranch Guide avec l’autorisation des auteurs. Pour plus d'informations sur ce livre ou pour en acheter un exemplaire, visitez le site bignerdranch.com.

3

Juste une autre réponse. Cette réponse ne montre que la version 5.0+, car je pense que la réponse de Doomknight publiée ici est la meilleure façon de procéder pour Android 4.4 et versions antérieures.

Ceci est publié à l'origine ici ( Y a-t-il un moyen d'obtenir la taille de la carte SD dans Android? ) par moi pour obtenir la taille de la carte SD externe sur Android 5.0+

Pour obtenir la carte SD externe en tant que File:

public File getExternalSdCard() {
    File externalStorage = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
        File storage = new File("/storage");

        if(storage.exists()) {
            File[] files = storage.listFiles();

            for (File file : files) {
                if (file.exists()) {
                    try {
                        if (Environment.isExternalStorageRemovable(file)) {
                            externalStorage = file;
                            break;
                        }
                    } catch (Exception e) {
                        Log.e("TAG", e.toString());
                    }
                }
            }
        }
    } else {
        // do one of many old methods
        // I believe Doomsknight's method is the best option here
    }

    return externalStorage;
}

Remarque: je ne reçois que la "première" carte SD externe, mais vous pouvez la modifier et renvoyer ArrayList<File> au lieu de File et laisser la boucle se poursuivre au lieu d'appeler break après avoir trouvé le premier. .

3

Ce sujet est un peu ancien, mais je cherchais une solution. Après quelques recherches, je suis arrivé avec le code ci-dessous pour récupérer une liste des points de montage "externes" disponibles qui, selon mes connaissances, fonctionnent sur de nombreux périphériques différents.

Fondamentalement, il lit les points de montage disponibles, filtre les points non valides, teste les autres s'ils sont accessibles et les ajoute si toutes les conditions sont remplies.

Bien entendu, les autorisations requises doivent être accordées avant que le code ne soit appelé.

// Notice: FileSystemDevice is just my own wrapper class. Feel free to replace it with your own. 

private List<FileSystemDevice> getDevices() {

    List<FileSystemDevice> devices = new ArrayList<>();

    // Add default external storage if available.
    File sdCardFromSystem = null;
    switch(Environment.getExternalStorageState()) {
        case Environment.MEDIA_MOUNTED:
        case Environment.MEDIA_MOUNTED_READ_ONLY:
        case Environment.MEDIA_SHARED:
            sdCardFromSystem = Environment.getExternalStorageDirectory();
            break;
    }

    if (sdCardFromSystem != null) {
        devices.add(new FileSystemDevice(sdCardFromSystem));
    }

    // Read /proc/mounts and add all mount points that are available
    // and are not "special". Also, check if the default external storage
    // is not contained inside the mount point. 
    try {
        FileInputStream fs = new FileInputStream("/proc/mounts");
        String mounts = IOUtils.toString(fs, "UTF-8");
        for(String line : mounts.split("\n")) {
            String[] parts = line.split(" ");

            // parts[0] - mount type
            // parts[1] - mount point
            if (parts.length > 1) {
                try {

                    // Skip "special" mount points and mount points that can be accessed
                    // directly by Android's functions. 
                    if (parts[0].equals("proc")) { continue; }
                    if (parts[0].equals("rootfs")) { continue; }
                    if (parts[0].equals("devpts")) { continue; }
                    if (parts[0].equals("none")) { continue; }
                    if (parts[0].equals("sysfs")) { continue; }
                    if (parts[0].equals("selinuxfs")) { continue; }
                    if (parts[0].equals("debugfs")) { continue; }
                    if (parts[0].equals("tmpfs")) { continue; }
                    if (parts[1].equals(Environment.getRootDirectory().getAbsolutePath())) { continue; }
                    if (parts[1].equals(Environment.getDataDirectory().getAbsolutePath())) { continue; }
                    if (parts[1].equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { continue; }

                    // Verify that the mount point is accessible by listing its content. 
                    File file = new File(parts[1]);
                    if (file.listFiles() != null) {
                        try {

                            // Get canonical path for case it's just symlink to another mount point.
                            String devPath = file.getCanonicalPath();

                            for(FileSystemDevice device : devices) {

                                if (!devices.contains(devPath)) {                        
                                    devices.add(new FileSystemDevice(new File(devPath)));
                                }

                            }
                        } catch (Exception e) {
                            // Silently skip the exception as it can only occur if the mount point is not valid. 
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    // Silently skip the exception as it can only occur if the mount point is not valid. 
                    e.printStackTrace();
                }
            }
        }

        fs.close();
    } catch (FileNotFoundException e) {
        // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable. 
        // Possibly, another detection method can be called here.
        e.printStackTrace();
    } catch (IOException e) {
        // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
        // Possibly, another detection method can be called here.
        e.printStackTrace();            
    }

    return devices;
}
0
Václav Hodek