wake-up-neo.net

Rufen Sie die Liste der Pfade aller mit einem Android-Gerät verbundenen Speichergeräte ab

Ich möchte die Liste aller Speichergeräte erhalten, die mit dem Android-Gerät verbunden sind.

Zum Beispiel - Interner Speicher (Speicher, in dem alle Ordner wie Downloads, DCIM usw. vorhanden sind), SD-Karte und OTG-Gerät.

Ich weiß, es gibt viele StackOverflow Beiträge, die dieses Thema diskutieren, aber keiner von ihnen könnte meinen Zweck erfüllen, wie oben angegeben.

Ich kann den internen Speicher durch Aufrufen von Environment.getExternalStorageDirectory().getPath() abrufen, die den Pfad zum internen Speicher zurückgeben.

Jede Hilfe hierzu wäre sehr dankbar, da es keine Standard-AFAIK gibt, mit der die Liste aller angeschlossenen Speichergeräte abgerufen werden kann.

Viele Lösungen funktionieren auch nicht auf verschiedenen Geräten und in der Android-Version.

14
Rahulrr2602

Sie können eine Klasse erstellen EnvironmentSDCardCheck

package com.example.storagecheck;

import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;
import Android.content.IntentFilter;
import Android.os.Build;
import Android.os.Environment;
import Android.os.storage.StorageManager;
import Android.support.v4.content.ContextCompat;
import Android.support.v4.os.EnvironmentCompat;
import Android.util.Log;

import Java.io.File;
import Java.io.IOException;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;
import Java.util.ArrayList;

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

    public final static String TYPE_PRIMARY = "primär";
    public final static String TYPE_INTERNAL = "intern";
    public final static String TYPE_SD = "MicroSD";
    public final static String TYPE_USB = "USB";
    public final static String TYPE_UNKNOWN = "unbekannt";

    public final static String WRITE_NONE = "none";
    public final static String WRITE_READONLY = "readonly";
    public final static String WRITE_APPONLY = "apponly";
    public final static String WRITE_FULL = "readwrite";

    private static Device[] devices, externalstorage, storage;
    private static BroadcastReceiver receiver;
    private static boolean useReceiver = true;
    private static String userDir;

    public static Device[] getDevices(Context context) {
        if (devices == null) initDevices(context);
        return devices;
    }

    public static Device[] getExternalStorage(Context context) {
        if (devices == null) initDevices(context);
        return externalstorage;
    }

    public static Device[] getStorage(Context context) {
        if (devices == null) initDevices(context);
        return storage;
    }

    public static IntentFilter getRescanIntentFilter() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL); 
        filter.addAction(Intent.ACTION_MEDIA_MOUNTED); 
        filter.addAction(Intent.ACTION_MEDIA_REMOVED); 
        filter.addAction(Intent.ACTION_MEDIA_SHARED); 
        filter.addDataScheme("file");
        return filter;
    }

    public static void setUseReceiver(Context context, boolean use) {
        if (use && receiver == null) {
            receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Log.i(TAG, "Storage " + intent.getAction() + "-" + intent.getData());
                    initDevices(context);
                }
            };
            context.registerReceiver(receiver, getRescanIntentFilter());
        } else if (!use && receiver != null) {
            context.unregisterReceiver(receiver);
            receiver = null;
        }
        useReceiver = use;
    }

    public static void initDevices(Context context) {
        if (userDir == null) userDir = "/Android/data/" + context.getPackageName();
        setUseReceiver(context, useReceiver);
        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        Class c = sm.getClass();
        Object[] vols;
        try {
            Method m = c.getMethod("getVolumeList", null);
            vols = (Object[]) m.invoke(sm, null); // Android.os.Storage.StorageVolume
            Device[] temp = new Device[vols.length];
            for (int i = 0; i < vols.length; i++) temp[i] = new Device(vols[i]);
            Device primary = null;
            for (Device d : temp) if (d.mPrimary) primary = d;
            if (primary == null) for (Device d : temp)
                if (!d.mRemovable) {
                    d.mPrimary = true;
                    primary = d;
                    break;
                }
            if (primary == null) {
                primary = temp[0];
                primary.mPrimary = true;
            }

            File[] files = ContextCompat.getExternalFilesDirs(context, null);
            File[] caches = ContextCompat.getExternalCacheDirs(context);
            for (Device d : temp) {
                if (files != null) for (File f : files)
                    if (f != null && f.getAbsolutePath().startsWith(d.getAbsolutePath()))
                        d.mFiles = f;
                if (caches != null) for (File f : caches)
                    if (f != null && f.getAbsolutePath().startsWith(d.getAbsolutePath()))
                        d.mCache = f;
            }

            ArrayList<Device> tempDev = new ArrayList<Device>(10);
            ArrayList<Device> tempStor = new ArrayList<Device>(10);
            ArrayList<Device> tempExt = new ArrayList<Device>(10);
            for (Device d : temp) {
                tempDev.add(d);
                if (d.isAvailable()) {
                    tempExt.add(d);
                    tempStor.add(d);
                }
            }

            Device internal = new Device(context);
            tempStor.add(0, internal); // bei Storage-Alternativen immer
            if (!primary.mEmulated) tempDev.add(0, internal); // bei Devices nur wenn zusätzlich

            devices = tempDev.toArray(new Device[tempDev.size()]);
            storage = tempStor.toArray(new Device[tempStor.size()]);
            externalstorage = tempExt.toArray(new Device[tempExt.size()]);
        } catch (Exception e) {
            // Fallback auf normale Android-Funktionen
        }

    }

    public static class Device extends File {
        String mUserLabel, mUuid, mState, mWriteState, mType;
        boolean mPrimary, mRemovable, mEmulated, mAllowMassStorage;
        long mMaxFileSize;
        File mFiles, mCache;

        Device(Context context) {
            super(Environment.getDataDirectory().getAbsolutePath());
            mState = Environment.MEDIA_MOUNTED;
            mFiles = context.getFilesDir();
            mCache = context.getCacheDir();
            mType = TYPE_INTERNAL;
            mWriteState = WRITE_APPONLY;
        }

        @SuppressWarnings("NullArgumentToVariableArgMethod")
        Device(Object storage) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            super((String) storage.getClass().getMethod("getPath", null).invoke(storage, null));
            for (Method m : storage.getClass().getMethods()) {
                if (m.getName().equals("getUserLabel") && m.getParameterTypes().length == 0 && m.getReturnType() == String.class)
                    mUserLabel = (String) m.invoke(storage, null); // ab Android 4.4
                if (m.getName().equals("getUuid") && m.getParameterTypes().length == 0 && m.getReturnType() == String.class)
                    mUuid = (String) m.invoke(storage, null); // ab Android 4.4
                if (m.getName().equals("getState") && m.getParameterTypes().length == 0 && m.getReturnType() == String.class)
                    mState = (String) m.invoke(storage, null); // ab Android 4.4
                if (m.getName().equals("isRemovable") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class)
                    mRemovable = (Boolean) m.invoke(storage, null); // ab Android 4.0
                if (m.getName().equals("isPrimary") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class)
                    mPrimary = (Boolean) m.invoke(storage, null); // ab Android 4.2
                if (m.getName().equals("isEmulated") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class)
                    mEmulated = (Boolean) m.invoke(storage, null); // ab Android 4.0
                if (m.getName().equals("allowMassStorage") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class)
                    mAllowMassStorage = (Boolean) m.invoke(storage, null); // ab Android 4.0
                if (m.getName().equals("getMaxFileSize") && m.getParameterTypes().length == 0 && m.getReturnType() == long.class)
                    mMaxFileSize = (Long) m.invoke(storage, null); // ab Android 4.0
                // getDescription (ab 4.1 mit context) liefert keine sinnvollen Werte
                // getPathFile (ab 4.2) liefert keine sinnvollen Werte
                // getMtpReserveSpace (ab 4.0) für diese Zwecke unwichtig
                // getStorageId (ab 4.0) für diese Zwecke unwichtig
            }
            if (mState == null) mState = getState();

            if (mPrimary)
                mType = TYPE_PRIMARY;
            else {
                String n = getAbsolutePath().toLowerCase();
                if (n.indexOf("sd") > 0)
                    mType = TYPE_SD;
                else if (n.indexOf("usb") > 0)
                    mType = TYPE_USB;
                else
                    mType = TYPE_UNKNOWN + " " + getAbsolutePath();
            }
        }

        public String getType() {
            return mType;
        }

        public String getAccess() {
            if (mWriteState == null) {
                try {
                    mWriteState = WRITE_NONE;
                    File[] root = listFiles();
                    if (root == null || root.length == 0)
                        throw new IOException("root empty/unreadable");
                    mWriteState = WRITE_READONLY;
                    File t = File.createTempFile("jow", null, getFilesDir());
                    //noinspection ResultOfMethodCallIgnored
                    t.delete();
                    mWriteState = WRITE_APPONLY;
                    t = File.createTempFile("jow", null, this);
                    //noinspection ResultOfMethodCallIgnored
                    t.delete();
                    mWriteState = WRITE_FULL;
                } catch (IOException ignore) {
                    Log.v(TAG, "test " + getAbsolutePath() + " ->" + mWriteState + "<- " + ignore.getMessage());
                }
            }
            return mWriteState;
        }

        public boolean isAvailable() {
            String s = getState();
            return (
                    Environment.MEDIA_MOUNTED.equals(s) ||
                            Environment.MEDIA_MOUNTED_READ_ONLY.equals(s)
            );
            // MEDIA_SHARED: als USB freigegeben; bitte Handy auf MTP umstellen
        }

        public String getState() {
            if (mRemovable || mState == null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop)
                    // Android 5.0? Da gibts was neues
                    mState = Environment.getExternalStorageState(this);
                else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat)
                    // Android 4.4? Dann dort nachfragen
                    mState = Environment.getStorageState(this);
                else if (canRead() && getTotalSpace() > 0)
                    // lesbar und Größe vorhanden => gibt es
                    mState = Environment.MEDIA_MOUNTED;
                else if (mState == null || Environment.MEDIA_MOUNTED.equals(mState))
                    // nicht lesbar, keine Größe aber noch MOUNTED || oder ungesetzt => UNKNOWN
                    mState = EnvironmentCompat.MEDIA_UNKNOWN;
            }
            return mState;
        }

        public File getFilesDir() {
            if (mFiles == null) {
                mFiles = new File(this, userDir + "/files");
                if (!mFiles.isDirectory())
                    //noinspection ResultOfMethodCallIgnored
                    mFiles.mkdirs();
            }
            return mFiles;
        }

        public File getCacheDir() {
            if (mCache == null) {
                mCache = new File(this, userDir + "/cache");
                if (!mCache.isDirectory())
                    //noinspection ResultOfMethodCallIgnored
                    mCache.mkdirs();
            }
            return mCache;
        }

        public boolean isPrimary() {
            return mPrimary;
        }

        public boolean isRemovable() {
            return mRemovable;
        }
        public boolean isEmulated() {
            return mEmulated;
        }

        public boolean isAllowMassStorage() {
            return mAllowMassStorage;
        }

        public long getMaxFileSize() {
            return mMaxFileSize;
        }

        public String getUserLabel() {
            return mUserLabel;
        }

        public String getUuid() {
            return mUuid;
        }
    }
}

und dann können Sie es verwenden, um zu überprüfen, ob eine SD-Karte oder ein USB-Stick oder ein unbekannter USB-Stick derzeit mit dem Gerät verbunden ist oder nicht

Auf diese Weise können Sie die angeschlossene SD-Karte, USB usw. erhalten.

private boolean checkSdCardPermission() {
    boolean flag = false;
    try {
        EnvironmentSDCard.Device[] devices = EnvironmentSDCard.getExternalStorage(MainActivity.this);
        for (EnvironmentSDCard.Device d : devices) {
            if (d.getType().equals(EnvironmentSDCard.TYPE_SD) || d.getType().contains(EnvironmentSDCard.TYPE_UNKNOWN) || d.getType().contains(EnvironmentSDCard.TYPE_USB)) {
                flag = d.isAvailable();
                if (flag)
                    break;
            }
        }
    } catch (Exception e) {
    }
    return flag;
}
0
Amjad Khan

Ich hatte etwas Glück mit

ContextCompat.getExternalFilesDirs

Dadurch können die Anwendungsordner auf externen Laufwerken gefunden werden. Ich habe noch keine bessere Lösung gefunden.

In meinem Anwendungsfall verwende ich Environment.DIRECTORY_MOVIES. Bei Bedarf gibt es jedoch andere Definitionen, einschließlich des generischen DIRECTORY_DOCUMENTS.

2
mksteve

Dies ist eine Ergänzung zu @ Sagars Antwort zum Abrufen von Reittieren von /proc. Beachten Sie die Verwendung von/proc/self/mountinfo anstelle von/proc/mountinfo oder/proc/mounts. Weitere Informationen zum Format von /proc/self/mountinfo finden Sie unter man 5 procfs. Während der folgende Code Dateien technisch analysiert, ist es sicher, auf dem Hauptthread ausgeführt zu werden (da /proc ein speicherinternes Dateisystem ist).

private static final int SANE_SIZE_LIMIT = 200 * 1024 * 1024;

// some hashmap for mapping long values to objects
// personally I am using HPPC maps, the HashMap class is for simplicity
public final HashMap<String> mountMap = new HashMap<>();

public void parse() {
    mountMap.clear();

    CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();

    parseMounts(decoder, true);
}

private int measure(FileChannel fc) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);

    int totalRead = 0, lastRead;

    do {
        buffer.clear();

        lastRead = fc.read(buffer);

        totalRead += lastRead;

        if (totalRead > SANE_SIZE_LIMIT) {
            throw new IOException("/proc/ file appears to be too big!!");
        }
    } while (lastRead != -1);

    fc.position(0);

    return totalRead;
}

private void parseMounts(CharsetDecoder d, boolean force) {
  File file = new File("/proc/self/mountinfo");

  int mode = ParcelFileDescriptor.MODE_READ_ONLY;

  try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, mode));
    FileChannel fc = new FileInputStream(pfd.getFileDescriptor()).getChannel()) {

    // Measure size of file before reading from it.
    // Virtual files in /proc/ appear to be zero-sized (because
    // their contents are dynamic), but we want to attempt
    // reading it in single read() call to avoid inconsistencies
    final int totalRead = measure(fc);

    try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor());
         Reader r = Channels.newReader(fis.getChannel(), d, totalRead);
         Scanner scanner = new Scanner(r)) {
      while (scanner.hasNextLine()) {
        scanner.nextInt();
        scanner.nextInt();

        final String[] mm = scanner.next().split(":");

        final int major = Integer.parseInt(mm[0]);
        final int minor = Integer.parseInt(mm[1]);

        final long dev_t = makedev(major, minor);

        final String source = scanner.next();

        // ignore bind-mounts for now
        if ("/".equals(source)) {
          final String location = scanner.next();

          // skip optional parts
          scanner.skip("(.+ -)");

          // type of file system (such as ext4)
          // most useful filesystems can be distinguished by type
          // but "Fuse" is problematic (because most Android
          // distributions implement dynamic permissions on
          // external SD card via custom Fuse filesystem).
          // To make matters worse, that SD card Fuse filesystem is
          // often mounted in several places at once. The code below
          // will throw away duplicate mounts by placing them in
          // HashMap, keyed by uniqie filesystem type ID,
          // but you can do it more cleverly be checking whether
          // a mountpoint directory is accessible (via File#list).
          // You can throw away rest of useless filesystems (such as
          // /mnt/secure/asec) by permission checks and blacklisting
          // well-known paths.
          final String fsType = scanner.next().intern();

          final String subject = scanner.next().intern();

          String created = location + subject + fsType;

          String prev = mountMap.put(dev_t, created);

          if (prev != null) {
            created.next = prev;
          }
        }

        scanner.nextLine();
      }

      return;
    } catch (NumberFormatException | NoSuchElementException nse) {
      // oops.. either a new row type was introduced (not a big deal)
      // or the file changed below our feet (because someone has mounted
      // something). Let's retry once more
      parseMounts(d, false);
    } catch (IOException e) {
        throw new WrappedIOException(e);
    }
}

Weitere nützliche Informationen (z. B. der allgemeine Pfad zum unbrauchbaren Dateisystem) finden Sie in dieser Antwort . Beachten Sie, dass die Formate von/proc/mounts und/proc/mountinfo sind unterschiedlich später eingeführt wurden, um das Format zu verbessern, ohne die Abwärtskompatibilität zu beeinträchtigen.

Der obige Code ist keine goldene Kugel - er sagt nichts über einzelne Dateisysteme aus, nur über ihre Pfade und den Namen des Dateisystems. Sie können zuversichtlich sein, dass "vfat" und "ext4" nützliche Dateisysteme sind und "procfs" nutzlos ist, aber so etwas wie "Fuse" wird rätselhaft bleiben. Sie können die Ausgabe des obigen Codes mit Android.os.storage.StorageManager erweitern, um benutzerfreundlichere Dateisystemnamen (wie "SD-Karte") zu erhalten, wenn diese verfügbar sind (Übereinstimmung anhand von Bereitstellungspfaden). Sie können auch StatFs verwenden, um den verfügbaren freien Speicherplatz auf der Partition abzurufen. Nutzlose virtuelle Dateisysteme geben bei einer Abfrage normalerweise keinen freien Speicherplatz und keinen verfügbaren Speicherplatz zurück. Wenn Sie dazu geneigt sind, können Sie die Mount-Optionen für das Dateisystem in Betracht ziehen, wenn Sie entscheiden, ob das Dateisystem dem Benutzer angezeigt werden soll. Z.B. ro vs rw, - Nur-Lese-Dateisystem-Mounts sind für Ihre Benutzer in der Regel weniger nützlich.


Wenn ich den Leuten diese Methode erkläre, sind sie oft besorgt über ihre Robustheit. Funktioniert sie auf einem zufälligen Junkphone? Bleibt es in zukünftigen Betriebssystemversionen verfügbar? Hier ist meine Meinung dazu: Diese Methode ist immer noch besser als viele reflexionsbasierte Empfehlungen - im schlimmsten Fall gibt das Lesen von/proc/file eine IOException zurück. Es führt nicht zum Absturz Ihrer App oder zu unvorhersehbarem Verhalten wie bei einigen reflexionsbasierten Hacks.

Das /proc-Dateisystem ist die offizielle Linux-API, die von Linux-Kernel-Entwicklern verwaltet wird. Es ist nicht möglich, es durch Angabe verschiedener Kernel-Erstellungsoptionen zu entfernen (z. B. ist es ein obligatorischer Bestandteil des Betriebssystemkernels). Es ist seit vielen Jahren verfügbar und bietet eine bessere Abwärtskompatibilität als die meisten Android-APIs. Insbesondere/proc/self/mountinfo wurde vor über 10 Jahren erstellt und wird in den meisten vorhandenen Android-Versionen außer den meisten älteren Versionen verfügbar sein.

Android-Entwickler unterstützen Linux-spezifische APIs nicht offiziell. Aber sie geben sich auch nicht die Mühe, sie zu zerbrechen. Einige der letzten SELinux-Änderungen in Android nach Lollipop haben den Zugriff auf einige Dateien in /proc/ eingeschränkt, da sie es Anwendungen ermöglichten, andere Anwendungen heimlich auszuspionieren. Diese Änderungen haben /proc/self zugänglich gehalten, da/proc/self nur anwendungsspezifische Informationen verfügbar macht (einschließlich Informationen zu Dateisystemen, die der Anwendung zur Verfügung stehen).

Wenn Google jemals von Linux zu Fuchensa oder einem anderen selbst entwickelten BSD-Zweig übergeht, werden die APIs/proc/und andere Linux-spezifische APIs wahrscheinlich nicht mehr funktionieren. Kümmert es mich? Nicht wirklich.

1
user1643723

Seit API Level 9 gibt es Android.os.storage.StorageManager. Rufen Sie getStorageVolumes() (verfügbar seit API-Stufe 24) auf, um eine Liste der Speichervolumes abzurufen. Wie der Doc es ausdrückt:

Gibt die Liste der freigegebenen/externen Speichervolumes zurück, die dem aktuellen Benutzer zur Verfügung stehen. Dies umfasst sowohl das primäre gemeinsam genutzte Speichergerät als auch alle angeschlossenen externen Volumes, einschließlich SD-Karten und USB-Sticks.

Das Ergebnis ist List<StorageVolume>. Schauen Sie sich jetzt Android.os.storage.StorageVolume an:

Informationen zu einem freigegebenen/externen Speichervolume für einen bestimmten Benutzer.

Sie können beispielsweise eine vom Benutzer sichtbare Beschreibung des Volumes erhalten, indem Sie getDescription() aufrufen. Siehe createAccessIntent(), wie Sie Zugriff erhalten.

0
Thomas