Suchen Sie den Speicherort einer austauschbaren SD-Karte

204

Gibt es eine universelle Möglichkeit, den Speicherort einer externen SD-Karte zu ermitteln?

Bitte nicht mit externem Speicher verwechseln .

Environment.getExternalStorageState()Gibt den Pfad zum internen SD-Mount-Punkt zurück, z. B. "/ mnt / sdcard". Die Frage betrifft jedoch die externe SD. Wie erhalte ich einen Pfad wie "/ mnt / sdcard / external_sd" (dieser kann von Gerät zu Gerät unterschiedlich sein)?

Ich denke, ich werde mit dem Filtern der Ausgabe des mountBefehls nach dem Namen des Dateisystems enden . Aber ich bin mir nicht sicher, ob dieser Weg robust genug ist.

borisstr
quelle
Hier ist meine Lösung, die bis Nougat funktioniert: stackoverflow.com/a/40205116/5002496
Gokul NC
'Environment.getExternalStorageState () gibt den Pfad zum internen SD-Mount-Punkt wie "/ mnt / sdcard" zurück.' Nun, es ist nicht intern in dem Sinne, dass Android den Begriff verwendet. Ich glaube, der Begriff, den Sie suchen, ist "nicht entfernbar".
LarsH

Antworten:

162

Environment.getExternalStorageState() Gibt den Pfad zum internen SD-Mount-Punkt wie "/ mnt / sdcard" zurück.

Nein, Environment.getExternalStorageDirectory()bezieht sich auf alles, was der Gerätehersteller als "externen Speicher" betrachtet. Bei einigen Geräten handelt es sich um Wechselmedien wie eine SD-Karte. Bei einigen Geräten ist dies ein Teil des Flashs auf dem Gerät. Hier bedeutet "externer Speicher" "das Material, auf das über den USB-Massenspeichermodus zugegriffen werden kann, wenn es auf einem Host-Computer bereitgestellt wird", zumindest für Android 1.x und 2.x.

Die Frage betrifft jedoch die externe SD. Wie erhalte ich einen Pfad wie "/ mnt / sdcard / external_sd" (er kann von Gerät zu Gerät unterschiedlich sein)?

Android hat kein Konzept für "externe SD", abgesehen von externem Speicher, wie oben beschrieben.

Wenn sich ein Gerätehersteller dafür entschieden hat, dass externer Speicher ein integrierter Flash-Speicher ist und auch über eine SD-Karte verfügt, müssen Sie sich an diesen Hersteller wenden, um festzustellen, ob Sie die SD-Karte verwenden können (nicht garantiert) und welche Regeln gelten Verwenden Sie es, z. B. welchen Pfad Sie dafür verwenden möchten.


AKTUALISIEREN

Zwei neue wichtige Dinge:

Erstens haben Sie unter Android 4.4+ keinen Schreibzugriff auf Wechselmedien (z. B. "externe SD"), mit Ausnahme von Speicherorten auf diesen Medien, die möglicherweise von getExternalFilesDirs()und zurückgegeben werden getExternalCacheDirs(). Siehe Dave Smiths ausgezeichnete Analyse an, insbesondere wenn Sie Details auf niedriger Ebene wünschen.

Zweitens, damit sich niemand darüber streiten kann, ob der Zugriff auf Wechselmedien ansonsten Teil des Android SDK ist oder nicht, hier die Einschätzung von Dianne Hackborn :

... denken Sie daran: Bis Android 4.4 hat die offizielle Android-Plattform SD-Karten überhaupt nicht unterstützt mit Ausnahme von zwei Sonderfällen: das alten Schule Speicherlayout in der externen Speicher ist eine SD - Karte (die noch von der Plattform heute unterstützt wird) und eine kleine Funktion, die Android 3.0 hinzugefügt wurde, um zusätzliche SD-Karten zu scannen und sie dem Medienanbieter hinzuzufügen und Apps schreibgeschützten Zugriff auf ihre Dateien zu gewähren (was auch heute noch auf der Plattform unterstützt wird).

Android 4.4 ist die erste Version der Plattform, mit der Anwendungen SD-Karten als Speicher verwenden können. Jeder vorherige Zugriff auf sie erfolgte über private, nicht unterstützte APIs. Wir haben jetzt eine ziemlich umfangreiche API auf der Plattform, die es Anwendungen ermöglicht, SD-Karten auf unterstützte Weise besser zu nutzen als bisher: Sie können ihren app-spezifischen Speicherbereich kostenlos nutzen, ohne dass dies erforderlich ist Berechtigungen in der App und können auf alle anderen Dateien auf der SD-Karte zugreifen, solange sie die Dateiauswahl durchlaufen, ohne dass spezielle Berechtigungen erforderlich sind.

CommonsWare
quelle
4
Und dieses Problem wird immer mehr zu einem Problem, da HC- und ICS-Geräte den Punkt "ExternalStorageDirectory" und alles andere für den internen physischen Speicher verwenden. Darüber hinaus haben die meisten Benutzer absolut keine Ahnung, wie sie feststellen können, wo sich ihre SD-Karte im Dateisystem befindet.
Tony Maro
283
Ihre Antwort lautet also im Grunde "Kontakt mit dem Hersteller". Nicht nützlich.
Drachenwurzel
6
Der letzte Teil der Antwort ist nicht ganz genau - es ist tatsächlich möglich, den Pfad der SD-Karte zu erkennen, indem Sie den Antworten darunter folgen (Scannen / proc / mounts, /system/etc/vold.fstab usw.).
Lernen Sie OpenGL ES
8
@CommonsWare: Trotzdem ist es immer noch nicht korrekt, dass man einen Hersteller "kontaktieren" muss, wenn es Lösungen gibt, die auf vielen Geräten funktionieren, und außerdem ist das SDK selbst nicht auf allen Geräten konsistent, so dass dies keine Garantie ist. Auch wenn diese Lösungen nicht auf allen Geräten funktionieren, funktionieren sie auf genügend Geräten, sodass viele Android-Apps auf dem Markt unter anderem auf diese Techniken zurückgreifen, um den Pfad der externen SD-Karte zu erkennen. Ich denke, es ist ein wenig hart und verfrüht, all diese Entwickler-Narren zu nennen - ist der Kunde nicht sicher der endgültige Richter darüber?
Lernen Sie OpenGL ES
5
@ CommonsWare Das ist fair genug, wie die Dinge gehen. Ich stimme Ihnen definitiv zu, dass ein Entwickler nicht davon ausgehen kann, dass dies immer überall funktioniert, und dass nicht garantiert werden kann, dass ein solcher Code auf allen Geräten oder für alle Android-Versionen funktioniert. Hoffentlich wird es im SDK behoben! In der Zwischenzeit gibt es immer noch Optionen, die auf vielen Geräten funktionieren und die Endbenutzererfahrung verbessern können. Wenn ich zwischen 80% Erfolg und 0% Erfolg wähle, nehme ich die 80%.
Lernen Sie OpenGL ES
64

Ich habe die folgende Lösung basierend auf einigen hier gefundenen Antworten gefunden.

CODE:

public class ExternalStorage {

    public static final String SD_CARD = "sdCard";
    public static final String EXTERNAL_SD_CARD = "externalSdCard";

    /**
     * @return True if the external storage is available. False otherwise.
     */
    public static boolean isAvailable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        }
        return false;
    }

    public static String getSdCardPath() {
        return Environment.getExternalStorageDirectory().getPath() + "/";
    }

    /**
     * @return True if the external storage is writable. False otherwise.
     */
    public static boolean isWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;

    }

    /**
     * @return A map of all storage locations available
     */
    public static Map<String, File> getAllStorageLocations() {
        Map<String, File> map = new HashMap<String, File>(10);

        List<String> mMounts = new ArrayList<String>(10);
        List<String> mVold = new ArrayList<String>(10);
        mMounts.add("/mnt/sdcard");
        mVold.add("/mnt/sdcard");

        try {
            File mountFile = new File("/proc/mounts");
            if(mountFile.exists()){
                Scanner scanner = new Scanner(mountFile);
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if (line.startsWith("/dev/block/vold/")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[1];

                        // don't add the default mount path
                        // it's already in the list.
                        if (!element.equals("/mnt/sdcard"))
                            mMounts.add(element);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            File voldFile = new File("/system/etc/vold.fstab");
            if(voldFile.exists()){
                Scanner scanner = new Scanner(voldFile);
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if (line.startsWith("dev_mount")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[2];

                        if (element.contains(":"))
                            element = element.substring(0, element.indexOf(":"));
                        if (!element.equals("/mnt/sdcard"))
                            mVold.add(element);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        for (int i = 0; i < mMounts.size(); i++) {
            String mount = mMounts.get(i);
            if (!mVold.contains(mount))
                mMounts.remove(i--);
        }
        mVold.clear();

        List<String> mountHash = new ArrayList<String>(10);

        for(String mount : mMounts){
            File root = new File(mount);
            if (root.exists() && root.isDirectory() && root.canWrite()) {
                File[] list = root.listFiles();
                String hash = "[";
                if(list!=null){
                    for(File f : list){
                        hash += f.getName().hashCode()+":"+f.length()+", ";
                    }
                }
                hash += "]";
                if(!mountHash.contains(hash)){
                    String key = SD_CARD + "_" + map.size();
                    if (map.size() == 0) {
                        key = SD_CARD;
                    } else if (map.size() == 1) {
                        key = EXTERNAL_SD_CARD;
                    }
                    mountHash.add(hash);
                    map.put(key, root);
                }
            }
        }

        mMounts.clear();

        if(map.isEmpty()){
                 map.put(SD_CARD, Environment.getExternalStorageDirectory());
        }
        return map;
    }
}

VERWENDUNG:

Map<String, File> externalLocations = ExternalStorage.getAllStorageLocations();
File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
File externalSdCard = externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);
Richard
quelle
1
getestet mit Nexus 4, Nexus s, Galaxie s2, Galaxie s3, HTC-Wunsch =)
Richard
2
Hallo nochmal, Richard - ob Sie es glauben oder nicht, ich muss fragen: Haben Sie tatsächlich versucht, auf diese Weise eine Datei zu schreiben und zurückzulesen, und nicht nur die Dirs zu bekommen? Erinnern Sie sich an unser altes "/ sdcard0" -Problem? Ich habe diesen Code ausprobiert und er ist auf einem S3 fehlgeschlagen, als ich versucht habe, die geschriebene Datei zurückzulesen. ... das ist sehr bizarr ... und schmerzhaft :))
Howard Pautz
10
Dies schlägt auf Geräten ohne 2 SD-Karten fehl. Es wird davon ausgegangen, dass der erste Fund intern und der zweite extern ist ...
Caner
Funktionierte NICHT für USB-Geräte, die über ein OTG-Kabel an Nexus 5 und Nexus 7 angeschlossen waren.
Khawar Raza
4
/system/etc/vold.fstab ist nicht verfügbar in Android 4.3+
Ali
37

Ich hatte eine Anwendung, die einen Ort verwendete, an ListPreferencedem der Benutzer den Ort auswählen musste, an dem er etwas speichern wollte.

In dieser App habe ich gescannt /proc/mountsund /system/etc/vold.fstabnach SD-Karten-Mount-Punkten gesucht. Ich habe die Mount-Punkte aus jeder Datei in zwei separaten ArrayLists gespeichert .

Dann habe ich eine Liste mit der anderen verglichen und Elemente verworfen, die nicht in beiden Listen enthalten waren. Das gab mir eine Liste von Root-Pfaden zu jeder SD-Karte.

Von dort testete ich die Wege mit File.exists(), File.isDirectory()und File.canWrite(). Wenn einer dieser Tests falsch war, habe ich diesen Pfad aus der Liste gestrichen.

Was auch immer in der Liste übrig war, ich habe es in ein String[]Array konvertiert, damit es vom ListPreferenceAttribut values ​​verwendet werden kann.

Sie können den Code hier anzeigen: http://sapienmobile.com/?p=204

Baron
quelle
Zu Ihrer
Information
1
@ 3c71 - Kannst du mir die Vold- und Mounts-Dateien für das Galaxy S3 schicken? Ich werde den Code optimieren, um ihn abzudecken.
Baron
Galaxy S, alle gefundenen Pfade waren nicht beschreibbar, komisch. Es wurden zwei Speicher gefunden, Standard / mnt / sdcard und / storage / sdcard0, beide konnten nicht getestet werden
fünfter
1
Ich habe den Code angepasst, um die Mounts-Datei zu ignorieren. Das war das Problem bei Motorola- und Samsung-Geräten. Die Mounts-Datei deckte nicht den Fall external_sd ab, sondern den in vold aufgeführten. In der ersten Version meiner Klasse wurden Reittiere mit alten und weggeworfenen Gegenständen verglichen, die beiden nicht gemeinsam waren. Holen Sie sich die aktualisierte Klasse über denselben Link oben.
Baron
1
Danke Baron, das ist "die" Antwort; Zumindest die nützliche.
Pstoppani
23

Sie können versuchen, die Support-Bibliotheksfunktion ContextCompat.getExternalFilesDirs () zu verwenden :

      final File[] appsDir=ContextCompat.getExternalFilesDirs(getActivity(),null);
      final ArrayList<File> extRootPaths=new ArrayList<>();
      for(final File file : appsDir)
        extRootPaths.add(file.getParentFile().getParentFile().getParentFile().getParentFile());

Der erste ist der primäre externe Speicher, und der Rest sollen echte SD-Kartenpfade sein.

Der Grund für das mehrfache ".getParentFile ()" besteht darin, einen anderen Ordner aufzurufen, da der ursprüngliche Pfad lautet

.../Android/data/YOUR_APP_PACKAGE_NAME/files/

BEARBEITEN: Hier ist eine umfassendere Methode, die ich erstellt habe, um die SD-Karten-Pfade zu erhalten:

  /**
   * returns a list of all available sd cards paths, or null if not found.
   *
   * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public static List<String> getSdCardPaths(final Context context, final boolean includePrimaryExternalStorage)
    {
    final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
    if(externalCacheDirs==null||externalCacheDirs.length==0)
      return null;
    if(externalCacheDirs.length==1)
      {
      if(externalCacheDirs[0]==null)
        return null;
      final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
      if(!Environment.MEDIA_MOUNTED.equals(storageState))
        return null;
      if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
        return null;
      }
    final List<String> result=new ArrayList<>();
    if(includePrimaryExternalStorage||externalCacheDirs.length==1)
      result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
    for(int i=1;i<externalCacheDirs.length;++i)
      {
      final File file=externalCacheDirs[i];
      if(file==null)
        continue;
      final String storageState=EnvironmentCompat.getStorageState(file);
      if(Environment.MEDIA_MOUNTED.equals(storageState))
        result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
      }
    if(result.isEmpty())
      return null;
    return result;
    }

  /** Given any file/folder inside an sd card, this will return the path of the sd card */
  private static String getRootOfInnerSdCardFolder(File file)
    {
    if(file==null)
      return null;
    final long totalSpace=file.getTotalSpace();
    while(true)
      {
      final File parentFile=file.getParentFile();
      if(parentFile==null||parentFile.getTotalSpace()!=totalSpace||!parentFile.canRead())
        return file.getAbsolutePath();
      file=parentFile;
      }
    }
Android-Entwickler
quelle
Es scheint eine sehr gute Antwort zu sein, aber wie würde man dies in eine einfache Aktivität integrieren? Es gibt mehrere Variablen nicht definiert, wie App, ContextCompact,EnvironmentCompact
Antonio
@Antonio ContextCompact, EnvironmentCompact sind über die Support-Bibliothek verfügbar. "App.global ()" ist nur der Anwendungskontext, den ich global festgelegt habe, da ich nicht überall gerne einen Kontextparameter hinzufüge.
Android-Entwickler
1
Toll! Funktioniert für mein Gerät v4.4 Samsung GT S Advance, hoffe, es wird für andere funktionieren
user25
@androiddeveloper Funktioniert die bearbeitete Antwort für alle Geräte- und SD-Kartengrößen?
Rahulrr2602
1
Das hat bei mir perfekt funktioniert - sollte die akzeptierte Antwort sein.
Paradox
17

Um alle externen Speicher abzurufen (unabhängig davon, ob es sich um SD-Karten oder interne nicht entfernbare Speicher handelt ), können Sie den folgenden Code verwenden:

final String state = Environment.getExternalStorageState();

if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...           
    //Retrieve the primary External Storage:
    final File primaryExternalStorage = Environment.getExternalStorageDirectory();

    //Retrieve the External Storages root directory:
    final String externalStorageRootDir;
    if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) {  // no parent...
        Log.d(TAG, "External Storage: " + primaryExternalStorage + "\n");
    }
    else {
        final File externalStorageRoot = new File( externalStorageRootDir );
        final File[] files = externalStorageRoot.listFiles();

        for ( final File file : files ) {
            if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) {  // it is a real directory (not a USB drive)...
                Log.d(TAG, "External Storage: " + file.getAbsolutePath() + "\n");
            }
        }
    }
}

Alternativ können Sie System.getenv ("EXTERNAL_STORAGE") verwenden , um das primäre externe Speicherverzeichnis (z. B. "/ storage / sdcard0" ) abzurufen, und System.getenv ("SECONDARY_STORAGE") , um die Liste aller sekundären Verzeichnisse (z. B. " / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ). Denken Sie daran, dass Sie auch in diesem Fall die Liste der sekundären Verzeichnisse filtern möchten, um die USB-Laufwerke auszuschließen.

Bitte beachten Sie auf jeden Fall, dass die Verwendung fest codierter Pfade immer ein schlechter Ansatz ist (insbesondere, wenn jeder Hersteller dies nach Belieben ändern kann).

Paolo Rovelli
quelle
2
Betrachten Sie jeden Downvoter, der keinen Kommentar hinterlässt, als Troll, also habe ich mich dafür entschieden, dies zu kompensieren. ;) ABER ich denke deine Methode ist ziemlich willkürlich: Wie können wir wissen, dass das Überspringen dieser "USB-Laufwerke", aber alles andere wirklich zu behalten, gleich "sdcards" ist, wie in der Frage gestellt? Auch Ihr Vorschlag System.getenv("SECONDARY_STORAGE")könnte auch einige Referenzen vertragen, da er nicht dokumentiert zu sein scheint.
Gr.
1
Soweit ich weiß, gibt es in der Android-API keinen Hinweis auf eine Standardmethode zum Abrufen aller externen Speicher. Das vorgeschlagene Verfahren ist jedoch überhaupt nicht willkürlich. In Android werden wie in jedem Unix / Linux-System ALLE Mounting-Speichergeräte in einem gemeinsamen Verzeichnis gespeichert / verknüpft: "/ mnt" (das Standard-Unix / Linux-Verzeichnis zum Mounten von Speichergeräten) oder in den neuesten Versionen "/ Lager". Aus diesem Grund können Sie ziemlich sicher sein, dass Sie alle in diesem Ordner verknüpften SD-Karten finden.
Paolo Rovelli
1
In Bezug auf die System.getenv-Methode ("EXTERNAL_STORAGE") habe ich keine Referenz anstelle der API-Seite (die nicht viel erklärt): developer.android.com/reference/java/lang/… Ich konnte keine finden offizielle Seite für die Umgebungsvariablen des Android-Systems. Hier finden Sie jedoch eine kurze Liste von ihnen: herongyang.com/Android/…
Paolo Rovelli
Was ich damit gemeint habe, dass ich mir bei /mntSD-Karten nicht sicher bin, ist, dass es auch verschiedene andere Fs-Bäume geben kann, nicht nur SD-Karten und USB-Laufwerke. Ihr Code würde auch alle internen (vielleicht sogar virtuellen) Dateisystem-Mounts auflisten, wenn ich es richtig verstehe, während die Frage nur SD-Karten will .
Gr.
1
Aha. Ja, du hast recht. Mit meiner Methode rufen Sie auch die internen (nicht entfernbaren) SD-Speicher ab.
Paolo Rovelli
15

Wie Richard verwende ich auch die Datei / proc / mounts , um die Liste der verfügbaren Speicheroptionen abzurufen

public class StorageUtils {

    private static final String TAG = "StorageUtils";

    public static class StorageInfo {

        public final String path;
        public final boolean internal;
        public final boolean readonly;
        public final int display_number;

        StorageInfo(String path, boolean internal, boolean readonly, int display_number) {
            this.path = path;
            this.internal = internal;
            this.readonly = readonly;
            this.display_number = display_number;
        }

        public String getDisplayName() {
            StringBuilder res = new StringBuilder();
            if (internal) {
                res.append("Internal SD card");
            } else if (display_number > 1) {
                res.append("SD card " + display_number);
            } else {
                res.append("SD card");
            }
            if (readonly) {
                res.append(" (Read only)");
            }
            return res.toString();
        }
    }

    public static List<StorageInfo> getStorageList() {

        List<StorageInfo> list = new ArrayList<StorageInfo>();
        String def_path = Environment.getExternalStorageDirectory().getPath();
        boolean def_path_internal = !Environment.isExternalStorageRemovable();
        String def_path_state = Environment.getExternalStorageState();
        boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
                                    || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        BufferedReader buf_reader = null;
        try {
            HashSet<String> paths = new HashSet<String>();
            buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
            String line;
            int cur_display_number = 1;
            Log.d(TAG, "/proc/mounts");
            while ((line = buf_reader.readLine()) != null) {
                Log.d(TAG, line);
                if (line.contains("vfat") || line.contains("/mnt")) {
                    StringTokenizer tokens = new StringTokenizer(line, " ");
                    String unused = tokens.nextToken(); //device
                    String mount_point = tokens.nextToken(); //mount point
                    if (paths.contains(mount_point)) {
                        continue;
                    }
                    unused = tokens.nextToken(); //file system
                    List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
                    boolean readonly = flags.contains("ro");

                    if (mount_point.equals(def_path)) {
                        paths.add(def_path);
                        list.add(0, new StorageInfo(def_path, def_path_internal, readonly, -1));
                    } else if (line.contains("/dev/block/vold")) {
                        if (!line.contains("/mnt/secure")
                            && !line.contains("/mnt/asec")
                            && !line.contains("/mnt/obb")
                            && !line.contains("/dev/mapper")
                            && !line.contains("tmpfs")) {
                            paths.add(mount_point);
                            list.add(new StorageInfo(mount_point, false, readonly, cur_display_number++));
                        }
                    }
                }
            }

            if (!paths.contains(def_path) && def_path_available) {
                list.add(0, new StorageInfo(def_path, def_path_internal, def_path_readonly, -1));
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (buf_reader != null) {
                try {
                    buf_reader.close();
                } catch (IOException ex) {}
            }
        }
        return list;
    }    
}
Vitaliy Polchuk
quelle
Vielen Dank. Hat perfekt funktioniert. Und ich mag die Art und Weise, wie Sie StorageInfo nicht veränderbar gemacht haben. Auf der anderen Seite printStackTrace? Wann haben wir android.util.Log.e?
Martin
1
Funktionierte NICHT für USB-Geräte, die über ein OTG-Kabel an Nexus 5 und Nexus 7 angeschlossen waren.
Khawar Raza
1
Ich kann dies nicht verwenden, um eine Datei auf SDCard zu schreiben
Eu Vid
Das gleiche Problem wie @EuVid funktioniert auf VM / AVD, aber nicht auf Hardware
Spion
11

Es ist möglich herauszufinden, wo zusätzliche SD-Karten eingelegt sind, indem Sie /proc/mounts(Standard-Linux-Datei) lesen und mit vold data ( /system/etc/vold.conf) abgleichen . Beachten Sie außerdem, dass der von zurückgegebene Speicherort Environment.getExternalStorageDirectory()möglicherweise nicht in der Vold-Konfiguration angezeigt wird (bei einigen Geräten kann der interne Speicher nicht abgemeldet werden), muss jedoch weiterhin in die Liste aufgenommen werden. Wir haben jedoch keine gute Möglichkeit gefunden, sie dem Benutzer zu beschreiben .

Jan Hudec
quelle
Imo ist die Verwendung von mountkompatibler als das Lesen des /procDateisystems. Das Problem ist, dass die SD-Karte nicht unbedingt als FAT formatiert sein muss. Der Kartenmontagepunkt kann auch von ROM zu ROM variieren. Es könnte auch mehrere andere VFAT-Partitionen geben ...
Borisstr
1
@borisstr: Hm, tatsächlich verwendet Android vold , daher ist es angemessen, auch die Konfiguration zu betrachten.
Jan Hudec
Die Codedatei, die ich in meinem obigen Beitrag freigegeben habe, enthält eine Methode zur Beschreibung der erkannten Stammpfade für den Benutzer. Schauen Sie sich die Methode setProperties () an .
Baron
1
@borisstr, nein, das Lesen von / proc / mounts ist auf Android-Geräten portabler als das Starten der mountausführbaren Datei, insbesondere da das Starten von ausführbaren Dateien nicht empfohlen wird.
Chris Stratton
7

Ich versuche diesmal alle Lösungen in diesem Thema. Auf Geräten mit einer externen (entfernbaren) und einer internen (nicht entfernbaren) Karte funktionierten jedoch nicht alle ordnungsgemäß. Pfad der externen Karte nicht möglich vom Befehl 'mount', von der Datei 'proc / mounts' usw.

Und ich erstelle meine eigene Lösung (bei Paulo Luan):

String sSDpath = null;
File   fileCur = null;
for( String sPathCur : Arrays.asList( "ext_card", "external_sd", "ext_sd", "external", "extSdCard",  "externalSdCard")) // external sdcard
{
   fileCur = new File( "/mnt/", sPathCur);
   if( fileCur.isDirectory() && fileCur.canWrite())
   {
     sSDpath = fileCur.getAbsolutePath();
     break;
   }
}
fileCur = null;
if( sSDpath == null)  sSDpath = Environment.getExternalStorageDirectory().getAbsolutePath();
Tapa Speichern
quelle
6

Wenn Sie sich den Quellcode android.os.Environmentansehen, werden Sie feststellen, dass Android in hohem Maße auf Umgebungsvariablen für Pfade angewiesen ist. Sie können die Umgebungsvariable "SECONDARY_STORAGE" verwenden, um den Pfad zur entfernbaren SD-Karte zu finden.

/**
 * Get a file using an environmental variable.
 *
 * @param variableName
 *         The Environment variable name.
 * @param paths
 *         Any paths to the file if the Environment variable was not found.
 * @return the File or {@code null} if the File could not be located.
 */
private static File getDirectory(String variableName, String... paths) {
    String path = System.getenv(variableName);
    if (!TextUtils.isEmpty(path)) {
        if (path.contains(":")) {
            for (String _path : path.split(":")) {
                File file = new File(_path);
                if (file.exists()) {
                    return file;
                }
            }
        } else {
            File file = new File(path);
            if (file.exists()) {
                return file;
            }
        }
    }
    if (paths != null && paths.length > 0) {
        for (String _path : paths) {
            File file = new File(_path);
            if (file.exists()) {
                return file;
            }
        }
    }
    return null;
}

Anwendungsbeispiel:

public static final File REMOVABLE_STORAGE = getDirectory("SECONDARY_STORAGE");
Jared Rummler
quelle
5

Verwenden Sie einfach dies:

String primary_sd = System.getenv("EXTERNAL_STORAGE");
if(primary_sd != null)
    Log.i("EXTERNAL_STORAGE", primary_sd);
String secondary_sd = System.getenv("SECONDARY_STORAGE");
if(secondary_sd != null)
    Log.i("SECONDARY_STORAGE", secondary_sd)
Behrouz.M
quelle
Auf einigen Geräten SECONDARY_STORAGEsind mehrere Pfade durch einen Doppelpunkt (":") getrennt. Deshalb habe ich den String geteilt (siehe meine Antwort oben).
Jared Rummler
Diese beiden geben für mich null zurück.
Tim Cooper
5

Gibt es eine universelle Möglichkeit, den Speicherort einer externen SD-Karte zu ermitteln?

Auf universelle Weise , wenn Sie offiziellen Weg meinen; ja es gibt eins.

In API - Ebene 19 , dh in Android - Version 4.4 Kitkat, haben sie aufgenommen File[] getExternalFilesDirs (String type)inContext der Klasse , die Anwendungen zum Speichern von Daten / Dateien in Micro - SD - Karten ermöglicht.

Android 4.4 ist die erste Version der Plattform, mit der Apps SD-Karten als Speicher verwenden können. Jeder Zugriff auf SD-Karten vor API-Stufe 19 erfolgte über private, nicht unterstützte APIs.

getExternalFilesDirs (String-Typ) gibt absolute Pfade zu anwendungsspezifischen Verzeichnissen auf allen gemeinsam genutzten / externen Speichergeräten zurück. Dies bedeutet, dass Pfade sowohl zum internen als auch zum externen Speicher zurückgegeben werden. Im Allgemeinen ist der zweite zurückgegebene Pfad der Speicherpfad für die microSD-Karte (falls vorhanden).

Beachten Sie jedoch, dass

Freigegebener Speicher ist möglicherweise nicht immer verfügbar, da Wechselmedien vom Benutzer ausgeworfen werden können. Der Medienstatus kann mit überprüft werden getExternalStorageState(File).

Mit diesen Dateien wird keine Sicherheit erzwungen. Beispielsweise kann jeder Anwendungsbestand WRITE_EXTERNAL_STORAGEin diese Dateien schreiben.

Die Terminologie für internen und externen Speicher gemäß Google / offiziellen Android-Dokumenten unterscheidet sich erheblich von unserer Meinung.

AnV
quelle
"Die Terminologie für internen und externen Speicher gemäß Google / offiziellen Android-Dokumenten unterscheidet sich erheblich von unserer Meinung." Ja, tatsächlich verdeutlicht der Titel der Frage, dass das OP nach einer austauschbaren SD-Karte fragt . getExternalFilesDirs()Gibt häufig SD-Karten zurück, die nicht austauschbar sind. Nein, dies ist keine universelle Methode, um den Speicherort einer austauschbaren SD-Karte zu ermitteln.
LarsH
"getExternalFilesDirs (String-Typ) gibt absolute Pfade zu anwendungsspezifischen Verzeichnissen auf allen gemeinsam genutzten / externen Speichergeräten zurück. Dies bedeutet, dass Pfade sowohl zum internen als auch zum externen Speicher zurückgegeben werden." Dieses Satzpaar ist sehr irreführend, denn damit beide wahr sind, muss "extern" zwei verschiedene und widersprüchliche Dinge bedeuten.
LarsH
4

Hier ist die Art und Weise, wie ich die externe Karte finde. Verwenden Sie mount cmd return und analysieren Sie dann den vfat-Teil.

String s = "";
try {
Process process = new ProcessBuilder().command("mount")
        .redirectErrorStream(true).start();

process.waitFor();

InputStream is = process.getInputStream();
byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
    s = s + new String(buffer);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}

//用行分隔mount列表
String[] lines = s.split("\n");
for(int i=0; i<lines.length; i++) {
//如果行内有挂载路径且为vfat类型,说明可能是内置或者外置sd的挂载点
if(-1 != lines[i].indexOf(path[0]) && -1 != lines[i].indexOf("vfat")) {
    //再用空格分隔
    String[] blocks = lines[i].split("\\s");
    for(int j=0; j<blocks.length; j++) {
        //判断是否是挂载为vfat类型
        if(-1 != blocks[j].indexOf(path[0])) {
            //Test if it is the external sd card.
        }
    }
}
}
Fakebear
quelle
4

Diese Lösung behandelt die Tatsache, dass System.getenv("SECONDARY_STORAGE")Marshmallow keinen Nutzen hat.

Getestet und bearbeitet an:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - Lager)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - Lager)
  • Samsung Galaxy S4 (Android 4.4 - Lager)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - Lager)

    /**
     * Returns all available external SD-Card roots in the system.
     *
     * @return paths to all available external SD-Card roots in the system.
     */
    public static String[] getStorageDirectories() {
        String [] storageDirectories;
        String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            List<String> results = new ArrayList<String>();
            File[] externalDirs = applicationContext.getExternalFilesDirs(null);
            for (File file : externalDirs) {
                String path = file.getPath().split("/Android")[0];
                if((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Environment.isExternalStorageRemovable(file))
                        || rawSecondaryStoragesStr != null && rawSecondaryStoragesStr.contains(path)){
                    results.add(path);
                }
            }
            storageDirectories = results.toArray(new String[0]);
        }else{
            final Set<String> rv = new HashSet<String>();
    
            if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
                final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
                Collections.addAll(rv, rawSecondaryStorages);
            }
            storageDirectories = rv.toArray(new String[rv.size()]);
        }
        return storageDirectories;
    }
DaveAlden
quelle
2

Seit meiner ursprünglichen Antwort oben ist das Scannen von Vold bei den verschiedenen Herstellern nicht mehr möglich.

Ich habe eine zuverlässigere und einfachere Methode entwickelt.

File mnt = new File("/storage");
if (!mnt.exists())
    mnt = new File("/mnt");

File[] roots = mnt.listFiles(new FileFilter() {

    @Override
    public boolean accept(File pathname) {
        return pathname.isDirectory() && pathname.exists()
                && pathname.canWrite() && !pathname.isHidden()
                && !isSymlink(pathname);
    }
});

Roots enthalten alle beschreibbaren Root-Verzeichnisse auf dem System, einschließlich aller über USB verbundenen USB-Geräte.

HINWEIS: Die canWrite-Methode benötigt die Berechtigung android.permission.WRITE_EXTERNAL_STORAGE.

Baron
quelle
Die Methode isSymlink (File) ist für den Typ new FileFilter () {}
Omid Omidi
Irgendeine Idee, ob dies aufgrund von canWrite keine externen SD-Karten unter Android 4.4 findet?
Anthony
Dies ist sicherlich einfacher als Ihre andere Methode, aber ist es zuverlässig? ZB habe ich gelesen, dass auf einigen Samsung-Geräten /external_sddie externe microSD-Karte ist; bei einigen LGs ist es /_ExternalSD; auf einigen Geräten ist es /sdcard. Vielleicht ist letzteres ein Symlink zu /storage/sdcard0oder ähnlich, aber werden diese anderen wirklich zuverlässig von /storage/*und abgedeckt /mount/*?
LarsH
pathname.canWrite()Ist es auch erforderlich, die Berechtigung WRITE_EXTERNAL_STORAGE zu verwenden und zu benötigen? Warum nicht einfach anrufen pathname.canRead()?
LarsH
1

Es war so spät, aber endlich habe ich etwas bekommen, das ich bei den meisten Geräten (nach Hersteller- und Android-Versionen) getestet habe. Es funktioniert unter Android 2.2+. Wenn Sie feststellen, dass es nicht funktioniert, kommentieren Sie es mit Ihrem Gerätenamen. ich werde es reparieren. Wenn jemand interessiert ist, werde ich erklären, wie es funktioniert.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;

import android.util.Log;


/**
 * @author ajeet
 *05-Dec-2014  2014
 *
 */
public class StorageUtil {

    public boolean isRemovebleSDCardMounted() {
        File file = new File("/sys/class/block/");
        File[] files = file.listFiles(new MmcblkFilter("mmcblk\\d$"));
        boolean flag = false;
        for (File mmcfile : files) {
            File scrfile = new File(mmcfile, "device/scr");
            if (scrfile.exists()) {
                flag = true;
                break;
            }
        }
        return flag;
    }

    public String getRemovebleSDCardPath() throws IOException {
        String sdpath = null;
        File file = new File("/sys/class/block/");
        File[] files = file.listFiles(new MmcblkFilter("mmcblk\\d$"));
        String sdcardDevfile = null;
        for (File mmcfile : files) {
            Log.d("SDCARD", mmcfile.getAbsolutePath());
            File scrfile = new File(mmcfile, "device/scr");
            if (scrfile.exists()) {
                sdcardDevfile = mmcfile.getName();
                Log.d("SDCARD", mmcfile.getName());
                break;
            }
        }
        if (sdcardDevfile == null) {
            return null;
        }
        FileInputStream is;
        BufferedReader reader;

        files = file.listFiles(new MmcblkFilter(sdcardDevfile + "p\\d+"));
        String deviceName = null;
        if (files.length > 0) {
            Log.d("SDCARD", files[0].getAbsolutePath());
            File devfile = new File(files[0], "dev");
            if (devfile.exists()) {
                FileInputStream fis = new FileInputStream(devfile);
                reader = new BufferedReader(new InputStreamReader(fis));
                String line = reader.readLine();
                deviceName = line;
            }
            Log.d("SDCARD", "" + deviceName);
            if (deviceName == null) {
                return null;
            }
            Log.d("SDCARD", deviceName);

            final File mountFile = new File("/proc/self/mountinfo");

            if (mountFile.exists()) {
                is = new FileInputStream(mountFile);
                reader = new BufferedReader(new InputStreamReader(is));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    // Log.d("SDCARD", line);
                    // line = reader.readLine();
                    // Log.d("SDCARD", line);
                    String[] mPonts = line.split("\\s+");
                    if (mPonts.length > 6) {
                        if (mPonts[2].trim().equalsIgnoreCase(deviceName)) {
                            if (mPonts[4].contains(".android_secure")
                                    || mPonts[4].contains("asec")) {
                                continue;
                            }
                            sdpath = mPonts[4];
                            Log.d("SDCARD", mPonts[4]);

                        }
                    }

                }
            }

        }

        return sdpath;
    }

    static class MmcblkFilter implements FilenameFilter {
        private String pattern;

        public MmcblkFilter(String pattern) {
            this.pattern = pattern;

        }

        @Override
        public boolean accept(File dir, String filename) {
            if (filename.matches(pattern)) {
                return true;
            }
            return false;
        }

    }

}
Ajeet47
quelle
Hey Downvoter, bitte versuchen Sie es zuerst. Wenn es nicht funktioniert, kommentieren Sie Ihr Gerät. Wir verwenden es über tausend Geräte mit Android 2.2+
Ajeet47
Ihre Klasse gibt mir / mnt / media_rw / extSdCard auf Samsung Galaxy S4, GT-I9500, Android 5.0.1 (Gerät ist NICHT gerootet). Aber im Ordner / mnt / media_rw mit ES File Manager ist nichts sichtbar ...
isabsent
@isabsent use if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT) {File [] file = context.getExternalFilesDirs (null); return file.length> 1? file [1]: null; }
Ajeet47
Was halten Sie von stackoverflow.com/a/27197248/753575 ? Ist dieser Ansatz für den Fall von Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT umfassender?
Isabsent
Ja. Es ist garantiert, dass Sie einen aussagekräftigen Pfad von context.getExternalFilesDirs (null) erhalten, aber Sie haben ihn für den Stammpfad gekürzt (er gibt den Pfad für Ihr App-Verzeichnis zurück. Trimmen Sie ihn auf "/ Android")
Ajeet47
1

Wenn Sie den folgenden Code schreiben, erhalten Sie den Speicherort:

/ storage / 663D-554E / Android / data / app_package_name / files /

Hier werden Ihre App-Daten unter / android / data in der SD-Karte gespeichert.

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

list[1]+"/fol" 

Um den Standort abzurufen, übergeben Sie 0 für intern und 1 für SD-Karte an Datei-Array.

Ich habe diesen Code auf einem Moto G4 Plus und einem Samsung-Gerät getestet (alles funktioniert einwandfrei).

hoffe das könnte hilfreich sein.

Rk215 Tech
quelle
Manchmal ist der
SD-Kartenpfad
1

Hier ist die Methode, mit der ich eine entfernbare finde SD-Karte . Es ist komplex und in einigen Situationen wahrscheinlich übertrieben, aber es funktioniert mit einer Vielzahl von Android-Versionen und Geräteherstellern, die ich in den letzten Jahren getestet habe. Ich kenne keine Geräte seit API-Level 15, auf denen die SD-Karte nicht gefunden wird, wenn eine gemountet ist. In den meisten Fällen werden keine Fehlalarme zurückgegeben, insbesondere wenn Sie den Namen einer bekannten Datei angeben, nach der gesucht werden soll.

Bitte lassen Sie mich wissen, wenn Sie auf Fälle stoßen, in denen es nicht funktioniert.

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.regex.Pattern;

public class SDCard {
    private static final String TAG = "SDCard";

    /** In some scenarios we can expect to find a specified file or folder on SD cards designed
     * to work with this app. If so, set KNOWNFILE to that filename. It will make our job easier.
     * Set it to null otherwise. */
    private static final String KNOWNFILE = null;

    /** Common paths for microSD card. **/
    private static String[] commonPaths = {
            // Some of these taken from
            // /programming/13976982/removable-storage-external-sdcard-path-by-manufacturers
            // These are roughly in order such that the earlier ones, if they exist, are more sure
            // to be removable storage than the later ones.
            "/mnt/Removable/MicroSD",
            "/storage/removable/sdcard1", // !< Sony Xperia Z1
            "/Removable/MicroSD", // Asus ZenPad C
            "/removable/microsd",
            "/external_sd", // Samsung
            "/_ExternalSD", // some LGs
            "/storage/extSdCard", // later Samsung
            "/storage/extsdcard", // Main filesystem is case-sensitive; FAT isn't.
            "/mnt/extsd", // some Chinese tablets, e.g. Zeki
            "/storage/sdcard1", // If this exists it's more likely than sdcard0 to be removable.
            "/mnt/extSdCard",
            "/mnt/sdcard/external_sd",
            "/mnt/external_sd",
            "/storage/external_SD",
            "/storage/ext_sd", // HTC One Max
            "/mnt/sdcard/_ExternalSD",
            "/mnt/sdcard-ext",

            "/sdcard2", // HTC One M8s
            "/sdcard1", // Sony Xperia Z
            "/mnt/media_rw/sdcard1",   // 4.4.2 on CyanogenMod S3
            "/mnt/sdcard", // This can be built-in storage (non-removable).
            "/sdcard",
            "/storage/sdcard0",
            "/emmc",
            "/mnt/emmc",
            "/sdcard/sd",
            "/mnt/sdcard/bpemmctest",
            "/mnt/external1",
            "/data/sdext4",
            "/data/sdext3",
            "/data/sdext2",
            "/data/sdext",
            "/storage/microsd" //ASUS ZenFone 2

            // If we ever decide to support USB OTG storage, the following paths could be helpful:
            // An LG Nexus 5 apparently uses usb://1002/UsbStorage/ as a URI to access an SD
            // card over OTG cable. Other models, like Galaxy S5, use /storage/UsbDriveA
            //        "/mnt/usb_storage",
            //        "/mnt/UsbDriveA",
            //        "/mnt/UsbDriveB",
    };

    /** Find path to removable SD card. */
    public static File findSdCardPath(Context context) {
        String[] mountFields;
        BufferedReader bufferedReader = null;
        String lineRead = null;

        /** Possible SD card paths */
        LinkedHashSet<File> candidatePaths = new LinkedHashSet<>();

        /** Build a list of candidate paths, roughly in order of preference. That way if
         * we can't definitively detect removable storage, we at least can pick a more likely
         * candidate. */

        // Could do: use getExternalStorageState(File path), with and without an argument, when
        // available. With an argument is available since API level 21.
        // This may not be necessary, since we also check whether a directory exists and has contents,
        // which would fail if the external storage state is neither MOUNTED nor MOUNTED_READ_ONLY.

        // I moved hard-coded paths toward the end, but we need to make sure we put the ones in
        // backwards order that are returned by the OS. And make sure the iterators respect
        // the order!
        // This is because when multiple "external" storage paths are returned, it's always (in
        // experience, but not guaranteed by documentation) with internal/emulated storage
        // first, removable storage second.

        // Add value of environment variables as candidates, if set:
        // EXTERNAL_STORAGE, SECONDARY_STORAGE, EXTERNAL_SDCARD_STORAGE
        // But note they are *not* necessarily *removable* storage! Especially EXTERNAL_STORAGE.
        // And they are not documented (API) features. Typically useful only for old versions of Android.

        String val = System.getenv("SECONDARY_STORAGE");
        if (!TextUtils.isEmpty(val)) addPath(val, null, candidatePaths);
        val = System.getenv("EXTERNAL_SDCARD_STORAGE");
        if (!TextUtils.isEmpty(val)) addPath(val, null, candidatePaths);

        // Get listing of mounted devices with their properties.
        ArrayList<File> mountedPaths = new ArrayList<>();
        try {
            // Note: Despite restricting some access to /proc (http://stackoverflow.com/a/38728738/423105),
            // Android 7.0 does *not* block access to /proc/mounts, according to our test on George's Alcatel A30 GSM.
            bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));

            // Iterate over each line of the mounts listing.
            while ((lineRead = bufferedReader.readLine()) != null) {
                Log.d(TAG, "\nMounts line: " + lineRead);
                mountFields = lineRead.split(" ");

                // columns: device, mountpoint, fs type, options... Example:
                // /dev/block/vold/179:97 /storage/sdcard1 vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0002,dmask=0002,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
                String device = mountFields[0], path = mountFields[1], fsType = mountFields[2];

                // The device, path, and fs type must conform to expected patterns.
                if (!(devicePattern.matcher(device).matches() &&
                        pathPattern.matcher(path).matches() &&
                        fsTypePattern.matcher(fsType).matches()) ||
                        // mtdblock is internal, I'm told.
                        device.contains("mtdblock") ||
                        // Check for disqualifying patterns in the path.
                        pathAntiPattern.matcher(path).matches()) {
                    // If this mounts line fails our tests, skip it.
                    continue;
                }

                // TODO maybe: check options to make sure it's mounted RW?
                // The answer at http://stackoverflow.com/a/13648873/423105 does.
                // But it hasn't seemed to be necessary so far in my testing.

                // This line met the criteria so far, so add it to candidate list.
                addPath(path, null, mountedPaths);
            }
        } catch (IOException ignored) {
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ignored) {
                }
            }
        }

        // Append the paths from mount table to candidate list, in reverse order.
        if (!mountedPaths.isEmpty()) {
            // See https://stackoverflow.com/a/5374346/423105 on why the following is necessary.
            // Basically, .toArray() needs its parameter to know what type of array to return.
            File[] mountedPathsArray = mountedPaths.toArray(new File[mountedPaths.size()]);
            addAncestors(candidatePaths, mountedPathsArray);
        }

        // Add hard-coded known common paths to candidate list:
        addStrings(candidatePaths, commonPaths);

        // If the above doesn't work we could try the following other options, but in my experience they
        // haven't added anything helpful yet.

        // getExternalFilesDir() and getExternalStorageDirectory() typically something app-specific like
        //   /storage/sdcard1/Android/data/com.mybackuparchives.android/files
        // so we want the great-great-grandparent folder.

        // This may be non-removable.
        Log.d(TAG, "Environment.getExternalStorageDirectory():");
        addPath(null, ancestor(Environment.getExternalStorageDirectory()), candidatePaths);

        // Context.getExternalFilesDirs() is only available from API level 19. You can use
        // ContextCompat.getExternalFilesDirs() on earlier APIs, but it only returns one dir anyway.
        Log.d(TAG, "context.getExternalFilesDir(null):");
        addPath(null, ancestor(context.getExternalFilesDir(null)), candidatePaths);

        // "Returns absolute paths to application-specific directories on all external storage
        // devices where the application can place persistent files it owns."
        // We might be able to use these to deduce a higher-level folder that isn't app-specific.
        // Also, we apparently have to call getExternalFilesDir[s](), at least in KITKAT+, in order to ensure that the
        // "external files" directory exists and is available.
        Log.d(TAG, "ContextCompat.getExternalFilesDirs(context, null):");
        addAncestors(candidatePaths, ContextCompat.getExternalFilesDirs(context, null));
        // Very similar results:
        Log.d(TAG, "ContextCompat.getExternalCacheDirs(context):");
        addAncestors(candidatePaths, ContextCompat.getExternalCacheDirs(context));

        // TODO maybe: use getExternalStorageState(File path), with and without an argument, when
        // available. With an argument is available since API level 21.
        // This may not be necessary, since we also check whether a directory exists,
        // which would fail if the external storage state is neither MOUNTED nor MOUNTED_READ_ONLY.

        // A "public" external storage directory. But in my experience it doesn't add anything helpful.
        // Note that you can't pass null, or you'll get an NPE.
        final File publicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        // Take the parent, because we tend to get a path like /pathTo/sdCard/Music.
        addPath(null, publicDirectory.getParentFile(), candidatePaths);
        // EXTERNAL_STORAGE: may not be removable.
        val = System.getenv("EXTERNAL_STORAGE");
        if (!TextUtils.isEmpty(val)) addPath(val, null, candidatePaths);

        if (candidatePaths.isEmpty()) {
            Log.w(TAG, "No removable microSD card found.");
            return null;
        } else {
            Log.i(TAG, "\nFound potential removable storage locations: " + candidatePaths);
        }

        // Accept or eliminate candidate paths if we can determine whether they're removable storage.
        // In Lollipop and later, we can check isExternalStorageRemovable() status on each candidate.
        if (Build.VERSION.SDK_INT >= 21) {
            Iterator<File> itf = candidatePaths.iterator();
            while (itf.hasNext()) {
                File dir = itf.next();
                // handle illegalArgumentException if the path is not a valid storage device.
                try {
                    if (Environment.isExternalStorageRemovable(dir)
                        // && containsKnownFile(dir)
                            ) {
                        Log.i(TAG, dir.getPath() + " is removable external storage");
                        return dir;
                    } else if (Environment.isExternalStorageEmulated(dir)) {
                        Log.d(TAG, "Removing emulated external storage dir " + dir);
                        itf.remove();
                    }
                } catch (IllegalArgumentException e) {
                    Log.d(TAG, "isRemovable(" + dir.getPath() + "): not a valid storage device.", e);
                }
            }
        }

        // Continue trying to accept or eliminate candidate paths based on whether they're removable storage.
        // On pre-Lollipop, we only have singular externalStorage. Check whether it's removable.
        if (Build.VERSION.SDK_INT >= 9) {
            File externalStorage = Environment.getExternalStorageDirectory();
            Log.d(TAG, String.format(Locale.ROOT, "findSDCardPath: getExternalStorageDirectory = %s", externalStorage.getPath()));
            if (Environment.isExternalStorageRemovable()) {
                // Make sure this is a candidate.
                // TODO: Does this contains() work? Should we be canonicalizing paths before comparing?
                if (candidatePaths.contains(externalStorage)
                    // && containsKnownFile(externalStorage)
                        ) {
                    Log.d(TAG, "Using externalStorage dir " + externalStorage);
                    return externalStorage;
                }
            } else if (Build.VERSION.SDK_INT >= 11 && Environment.isExternalStorageEmulated()) {
                Log.d(TAG, "Removing emulated external storage dir " + externalStorage);
                candidatePaths.remove(externalStorage);
            }
        }

        // If any directory contains our special test file, consider that the microSD card.
        if (KNOWNFILE != null) {
            for (File dir : candidatePaths) {
                Log.d(TAG, String.format(Locale.ROOT, "findSdCardPath: Looking for known file in candidate path, %s", dir));
                if (containsKnownFile(dir)) return dir;
            }
        }

        // If we don't find the known file, still try taking the first candidate.
        if (!candidatePaths.isEmpty()) {
            Log.d(TAG, "No definitive path to SD card; taking the first realistic candidate.");
            return candidatePaths.iterator().next();
        }

        // If no reasonable path was found, give up.
        return null;
    }

    /** Add each path to the collection. */
    private static void addStrings(LinkedHashSet<File> candidatePaths, String[] newPaths) {
        for (String path : newPaths) {
            addPath(path, null, candidatePaths);
        }
    }

    /** Add ancestor of each File to the collection. */
    private static void addAncestors(LinkedHashSet<File> candidatePaths, File[] files) {
        for (int i = files.length - 1; i >= 0; i--) {
            addPath(null, ancestor(files[i]), candidatePaths);
        }
    }

    /**
     * Add a new candidate directory path to our list, if it's not obviously wrong.
     * Supply path as either String or File object.
     * @param strNew path of directory to add (or null)
     * @param fileNew directory to add (or null)
     */
    private static void addPath(String strNew, File fileNew, Collection<File> paths) {
        // If one of the arguments is null, fill it in from the other.
        if (strNew == null) {
            if (fileNew == null) return;
            strNew = fileNew.getPath();
        } else if (fileNew == null) {
            fileNew = new File(strNew);
        }

        if (!paths.contains(fileNew) &&
                // Check for paths known not to be removable SD card.
                // The antipattern check can be redundant, depending on where this is called from.
                !pathAntiPattern.matcher(strNew).matches()) {

            // Eliminate candidate if not a directory or not fully accessible.
            if (fileNew.exists() && fileNew.isDirectory() && fileNew.canExecute()) {
                Log.d(TAG, "  Adding candidate path " + strNew);
                paths.add(fileNew);
            } else {
                Log.d(TAG, String.format(Locale.ROOT, "  Invalid path %s: exists: %b isDir: %b canExec: %b canRead: %b",
                        strNew, fileNew.exists(), fileNew.isDirectory(), fileNew.canExecute(), fileNew.canRead()));
            }
        }
    }

    private static final String ANDROID_DIR = File.separator + "Android";

    private static File ancestor(File dir) {
        // getExternalFilesDir() and getExternalStorageDirectory() typically something app-specific like
        //   /storage/sdcard1/Android/data/com.mybackuparchives.android/files
        // so we want the great-great-grandparent folder.
        if (dir == null) {
            return null;
        } else {
            String path = dir.getAbsolutePath();
            int i = path.indexOf(ANDROID_DIR);
            if (i == -1) {
                return dir;
            } else {
                return new File(path.substring(0, i));
            }
        }
    }

    /** Returns true iff dir contains the special test file.
     * Assumes that dir exists and is a directory. (Is this a necessary assumption?) */
    private static boolean containsKnownFile(File dir) {
        if (KNOWNFILE == null) return false;

        File knownFile = new File(dir, KNOWNFILE);
        return knownFile.exists();
    }

    private static Pattern
            /** Pattern that SD card device should match */
            devicePattern = Pattern.compile("/dev/(block/.*vold.*|fuse)|/mnt/.*"),
    /** Pattern that SD card mount path should match */
    pathPattern = Pattern.compile("/(mnt|storage|external_sd|extsd|_ExternalSD|Removable|.*MicroSD).*",
            Pattern.CASE_INSENSITIVE),
    /** Pattern that the mount path should not match.
     * 'emulated' indicates an internal storage location, so skip it.
     * 'asec' is an encrypted package file, decrypted and mounted as a directory. */
    pathAntiPattern = Pattern.compile(".*(/secure|/asec|/emulated).*"),
    /** These are expected fs types, including vfat. tmpfs is not OK.
     * fuse can be removable SD card (as on Moto E or Asus ZenPad), or can be internal (Huawei G610). */
    fsTypePattern = Pattern.compile(".*(fat|msdos|ntfs|ext[34]|fuse|sdcard|esdfs).*");
}

PS

  • Vergiss nicht <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />im Manifest. Stellen Sie auf API-Ebene 23 und höher sicher, dass Sie verwendencheckSelfPermission / verwenden requestPermissions.
  • Setzen Sie KNOWNFILE = "myappfile", wenn sich eine Datei oder ein Ordner auf der SD-Karte befindet. Das macht die Erkennung genauer.
  • Natürlich möchten Sie den Wert von zwischenspeichern findSdCardPath(),Natürlich anstatt ihn jedes Mal neu zu berechnen, wenn Sie ihn benötigen.
  • Der Log.d()obige Code enthält eine Reihe von logging ( ). Es hilft bei der Diagnose von Fällen, in denen der richtige Pfad nicht gefunden wurde. Kommentieren Sie es aus, wenn Sie keine Protokollierung wünschen.
LarsH
quelle
Downvoters, können Sie eine Möglichkeit vorschlagen, diese Antwort zu verbessern?
LarsH
1

Die einzige funktionierende Lösung, die ich gefunden habe, war diese, die Reflexion verwendet

 /**
 * Get external sd card path using reflection
 * @param mContext
 * @param is_removable is external storage removable
 * @return
 */
private static String getExternalStoragePath(Context mContext, boolean is_removable) {

    StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
    Class<?> storageVolumeClazz = null;
    try {
        storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
        Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
        Method getPath = storageVolumeClazz.getMethod("getPath");
        Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
        Object result = getVolumeList.invoke(mStorageManager);
        final int length = Array.getLength(result);
        for (int i = 0; i < length; i++) {
            Object storageVolumeElement = Array.get(result, i);
            String path = (String) getPath.invoke(storageVolumeElement);
            boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement);
            if (is_removable == removable) {
                return path;
            }
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}
Habib Kazemi
quelle
Ich persönlich bevorzuge die Verwendung von Reflection nicht, da Google die Abwärtskompatibilität in neuen Android-Versionen nicht schätzt!
Behrouz.M
0

Ich weiß nicht warum, aber ich muss .createNewFile () für eine Datei aufrufen, die in den öffentlichen Speicherverzeichnissen erstellt wurde, bevor ich sie verwenden kann. Im Rahmen sagen die Kommentare für diese Methode, dass es nicht nützlich ist. Hier ist ein Beispiel ...


 String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS) + File.separator + "My Directory";
            final File myDir = new File(myPath);
            try {
                myDir.mkdirs();
            } catch (Exception ex) {
                Toast.makeText(this, "error: " + ex.getMessage(), Toast.LENGTH_LONG).show();
            }

        String fname = "whatever";
        File newFile = new File(myDir, fname);

        Log.i(TAG, "File exists --> " + newFile.exists()) //will be false  
    try {
            if (newFile.createNewFile()) {

                 //continue 

              } else {

                Log.e(TAG, "error creating file");

            }

        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }

user1743524
quelle
0

Ich habe eine Utils-Methode erstellt, um zu überprüfen, ob eine SD-Karte auf dem Gerät verfügbar ist oder nicht, und um den SD-Kartenpfad auf dem Gerät abzurufen, falls verfügbar.

Sie können zwei Methoden unten in die Klasse Ihres Projekts kopieren, die Sie benötigen. Das ist alles.

public String isRemovableSDCardAvailable() {
    final String FLAG = "mnt";
    final String SECONDARY_STORAGE = System.getenv("SECONDARY_STORAGE");
    final String EXTERNAL_STORAGE_DOCOMO = System.getenv("EXTERNAL_STORAGE_DOCOMO");
    final String EXTERNAL_SDCARD_STORAGE = System.getenv("EXTERNAL_SDCARD_STORAGE");
    final String EXTERNAL_SD_STORAGE = System.getenv("EXTERNAL_SD_STORAGE");
    final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");

    Map<Integer, String> listEnvironmentVariableStoreSDCardRootDirectory = new HashMap<Integer, String>();
    listEnvironmentVariableStoreSDCardRootDirectory.put(0, SECONDARY_STORAGE);
    listEnvironmentVariableStoreSDCardRootDirectory.put(1, EXTERNAL_STORAGE_DOCOMO);
    listEnvironmentVariableStoreSDCardRootDirectory.put(2, EXTERNAL_SDCARD_STORAGE);
    listEnvironmentVariableStoreSDCardRootDirectory.put(3, EXTERNAL_SD_STORAGE);
    listEnvironmentVariableStoreSDCardRootDirectory.put(4, EXTERNAL_STORAGE);

    File externalStorageList[] = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        externalStorageList = getContext().getExternalFilesDirs(null);
    }
    String directory = null;
    int size = listEnvironmentVariableStoreSDCardRootDirectory.size();
    for (int i = 0; i < size; i++) {
        if (externalStorageList != null && externalStorageList.length > 1 && externalStorageList[1] != null)
            directory = externalStorageList[1].getAbsolutePath();
        else
            directory = listEnvironmentVariableStoreSDCardRootDirectory.get(i);

        directory = canCreateFile(directory);
        if (directory != null && directory.length() != 0) {
            if (i == size - 1) {
                if (directory.contains(FLAG)) {
                    Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
                    return directory;
                } else {
                    return null;
                }
            }
            Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
            return directory;
        }
    }
    return null;
}

/**
 * Check if can create file on given directory. Use this enclose with method
 * {@link BeginScreenFragement#isRemovableSDCardAvailable()} to check sd
 * card is available on device or not.
 * 
 * @param directory
 * @return
 */
public String canCreateFile(String directory) {
    final String FILE_DIR = directory + File.separator + "hoang.txt";
    File tempFlie = null;
    try {
        tempFlie = new File(FILE_DIR);
        FileOutputStream fos = new FileOutputStream(tempFlie);
        fos.write(new byte[1024]);
        fos.flush();
        fos.close();
        Log.e(getClass().getSimpleName(), "Can write file on this directory: " + FILE_DIR);
    } catch (Exception e) {
        Log.e(getClass().getSimpleName(), "Write file error: " + e.getMessage());
        return null;
    } finally {
        if (tempFlie != null && tempFlie.exists() && tempFlie.isFile()) {
            // tempFlie.delete();
            tempFlie = null;
        }
    }
    return directory;
}
user3161772
quelle
-1

Es funktioniert für alle externen Geräte. Stellen Sie jedoch sicher, dass Sie nur den Namen des externen Geräteordners abrufen. Anschließend müssen Sie die Datei mithilfe der Dateiklasse von einem bestimmten Speicherort abrufen.

public static List<String> getExternalMounts() {
        final List<String> out = new ArrayList<>();
        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;
    }

Berufung:

List<String> list=getExternalMounts();
        if(list.size()>0)
        {
            String[] arr=list.get(0).split("/");
            int size=0;
            if(arr!=null && arr.length>0) {
                size= arr.length - 1;
            }
            File parentDir=new File("/storage/"+arr[size]);
            if(parentDir.listFiles()!=null){
                File parent[] = parentDir.listFiles();

                for (int i = 0; i < parent.length; i++) {

                    // get file path as parent[i].getAbsolutePath());

                }
            }
        }

Zugriff auf externen Speicher

Um Dateien auf dem externen Speicher lesen oder schreiben zu können, muss Ihre App die Systemberechtigungen READ_EXTERNAL_STORAGE oder WRITE_EXTERNAL_STORAGE erwerben . Beispielsweise:

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>
Neeraj Singh
quelle
-2

/ sdcard => Interner Speicher (Es ist ein Symlink, sollte aber funktionieren)

/ mnt / extSdCard => Externe SD-Karte

Dies ist für Samsung Galaxy S3

Sie können sich wahrscheinlich darauf verlassen, dass dies für die meisten gilt ... überprüfen Sie es jedoch noch einmal!

Robbyoconnor
quelle
8
Ich hatte mehrere verschiedene Android-Handys, etwa die Hälfte davon Samsung, und habe diesen Ort noch nie benutzt. Es mag auf dem S3 wahr sein, aber zu sagen "Sie können sich wahrscheinlich darauf verlassen, dass dies für die meisten wahr ist" ist völlig falsch.
Geobits
falsch. /sdcardist ein Symlink zu extern auf meinem Sony 2305.
Jiggunjer
2
Habe ich nicht gesagt, dass es nicht sein könnte?
Robbyoconnor