Çıkarılabilir bir SD kartın yerini bulma


205

Harici bir SD kartın yerini bulmanın evrensel bir yolu var mı?

Lütfen Harici Depolama ile karıştırmayın .

Environment.getExternalStorageState()"/ mnt / sdcard" gibi dahili SD bağlama noktasının yolunu döndürür. Ancak soru harici SD ile ilgili. "/ Mnt / sdcard / external_sd" gibi bir yolu nasıl alabilirim (cihazdan cihaza değişiklik gösterebilir)?

Sanırım mountkomutun çıktısını dosya sistemi adına göre filtreleyerek bitireceğim . Ama bu yolun yeterince sağlam olduğundan emin değilim.


İşte Nougat'a kadar çalışan çözümüm: stackoverflow.com/a/40205116/5002496
Gokul NC

'Environment.getExternalStorageState (), "/ mnt / sdcard" gibi dahili SD bağlama noktasına giden yolu döndürür. " Android'in bu terimi kullandığı anlamında içsel değil. Aradığınız terimin "kaldırılamaz" olduğuna inanıyorum.
LarsH

Yanıtlar:


162

Environment.getExternalStorageState() "/ mnt / sdcard" gibi dahili SD bağlama noktasına giden yolu döndürür

Hayır, Environment.getExternalStorageDirectory()cihaz üreticisi "harici depolama" olarak kabul edilen her şeyi ifade eder. Bazı cihazlarda bu, SD kart gibi çıkarılabilir bir ortamdır. Bazı cihazlarda, bu cihaz üzerindeki flaşın bir kısmıdır. Burada, "harici depolama birimi" en azından Android 1.x ve 2.x için "bir ana makineye monte edildiğinde USB Yığın Depolama modu üzerinden erişilebilen öğeler" anlamına gelir.

Ancak soru harici SD ile ilgili. "/ Mnt / sdcard / external_sd" gibi bir yol nasıl elde edilir (cihazdan cihaza değişiklik gösterebilir)?

Android, yukarıda açıklandığı gibi harici depolama biriminin yanı sıra "harici SD" kavramına sahip değildir.

Bir cihaz üreticisi harici depolama biriminin yerleşik flaş olmasını seçtiyse ve ayrıca bir SD kartına sahipse, SD kartı kullanıp kullanamayacağınızı (garanti edilmez) ve kuralların ne için olduğunu belirlemek için bu üreticiyle iletişime geçmeniz gerekir. bunun için hangi yolu kullanacağınız gibi.


GÜNCELLEME

Son iki not:

İlk olarak, Android 4.4 ve sonraki sürümlerinde, çıkarılabilir medyaya (örneğin, "harici SD") yazma erişiminiz yoktur, bu medyadaki getExternalFilesDirs()ve tarafından döndürülebilecek yerler hariç getExternalCacheDirs(). Bkz Dave Smith'in mükemmel analizini düşük seviyesinde ayrıntıları istiyorum özellikle, bunun yüzünden.

İkincisi, çıkarılabilir medya erişiminin Android SDK'nın başka bir parçası olup olmadığı konusunda tartışmaya varmamak için , Dianne Hackborn'un değerlendirmesi :

... akılda tutmak: Android 4.4 kadar, resmi Android platformu en SD kartları destek vermedi tüm iki özel durumlar haricinde: harici depolama (bugün hala platformu tarafından desteklenir) SD karttır eski okul depolama düzeni ve Android 3.0'a ek SD kartlarını tarayacak ve medya sağlayıcısına ekleyecek ve uygulamalara dosyalarına salt okunur erişim sağlayacak küçük bir özellik eklendi (bu da bugün hala platformda desteklenmektedir).

Android 4.4, uygulamaların depolama için SD kartları kullanmasına izin veren platformun ilk sürümüdür. Bundan önce bunlara her türlü erişim özel, desteklenmeyen API'lerle gerçekleştirildi. Artık platformda, uygulamaların SD kartlarını daha önce olduğundan daha iyi bir şekilde desteklenen bir şekilde kullanmasına izin veren oldukça zengin bir API'miz var: uygulamaya özel depolama alanlarını herhangi bir gereksinim olmadan ücretsiz olarak kullanabilirler ve SD karttaki diğer dosyalara dosya seçiciden geçtikleri sürece, özel izinlere gerek kalmadan tekrar erişebilirler.


4
HC ve ICS cihazları "ExternalStorageDirectory" ve diğer her şeyi dahili fiziksel depolamaya çıktıkça bu sorun daha da önemli hale geliyor. Çoğu kullanıcının sdcard'ının dosya sisteminde nerede olduğunu nasıl bulacağına dair hiçbir ipucu olmadığına dikkat edin.
Tony Maro

284
Yani cevabınız temelde 'üreticiye başvurun'. Yararlı değil.
dragonroot

6
Cevabın son kısmı tam olarak doğru değil - SD kart yolunu, aşağıdaki cevapları (tarama / proc / bağlar, /system/etc/vold.fstab, vb ...) takip ederek tespit etmek gerçekten mümkündür.
OpenGL ES'yi öğrenin

8
@CommonsWare: Bununla birlikte, orada birçok cihazda çalışan çözümler olduğunda bir üreticiyle iletişim kurmanın "ihtiyacı" olduğu doğru değildir ve SDK'nın kendisi tüm cihazlarda tutarlı değildir, bu nedenle garanti yoktur. Bu çözümler tüm cihazlarda çalışmıyor olsa bile, piyasadaki birçok Android uygulamasının harici SD kart yolunu algılamak için bu tekniklere güvendiği yeterli cihazlarda çalışırlar. Bence bu geliştiricilerin hepsini aptallara çağırmak biraz sert ve erken - müşteri kesinlikle bunun nihai yargıcı değil mi?
OpenGL ES

5
@CommonsWare İşler ilerledikçe bu yeterince adil. Bir geliştiricinin bunun her zaman her yerde çalışacağını varsayamayacağına ve böyle bir kodun tüm cihazlarda veya Android'in tüm sürümlerinde çalışacağının garanti edilemeyeceğine kesinlikle katılıyorum. Umarım SDK'da sabitlenir! Bu arada, birçok cihazda çalışan ve son kullanıcı deneyimini geliştirebilecek seçenekler var ve% 80 başarı ile% 0 başarı arasında seçim yaptığım için% 80 alacağım.
OpenGL ES

64

Burada bulunan bazı cevaplara dayanarak aşağıdaki çözümü buldum.

KODU:

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;
    }
}

KULLANIM:

Map<String, File> externalLocations = ExternalStorage.getAllStorageLocations();
File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
File externalSdCard = externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);

1
nexus 4, nexus s, galaxy s2, galaxy s3, htc desire =) ile test edildi
Richard

2
Tekrar merhaba, Richard - inan ya da inanma, sormak zorundayım: Aslında sadece bir dosyayı yazmayı ve bu şekilde tekrar okumayı denediniz mi, sadece dirs almayı değil mi? Eski "/ sdcard0" sorunumuzu hatırlıyor musunuz? Bu kodu denedim ve yazdığı dosyayı tekrar okumaya çalıştığımda bir S3'te başarısız oldu . ... bu çok tuhaf ... ve acı verici :))
Howard Pautz

10
Bu, 2 SD kartı olmayan cihazlarda başarısız olur. Bulunan 1. dahili, 2. bulunan harici olduğunu varsayar ...
Caner

Nexus 5 ve Nexus 7'de OTG kablosuyla takılan USB cihazları için
ÇALIŞMADI

4
/system/etc/vold.fstab, android 4.3 ve sonraki sürümlerinde erişilemez
Ali

37

ListPreferenceKullanıcının bir şey kaydetmek istedikleri yeri seçmek için gerekli olduğu bir yerde kullandım .

Bu uygulamada, tarama yaptım /proc/mounts/system/etc/vold.fstab taradım ve sdcard bağlama noktaları için. Her dosyadaki bağlama noktalarını iki ayrı dosyada ArrayListsakladım.

Sonra, bir listeyi diğer listeyle karşılaştırdım ve her iki listede olmayan öğeleri de attım. Bu bana her sdcard için kök yolların bir listesini verdi.

Oradan, ben yolları test ile File.exists(), File.isDirectory()veFile.canWrite() . Bu testlerden herhangi biri yanlışsa, bu yolu listeden attım.

Listede ne olursa olsun, bir String[]diziye dönüştürdüm, böyleceListPreference değerler özniteliği .

Kodu buradan görüntüleyebilirsiniz: http://sapienmobile.com/?p=204


FYI, bu Galaxy S3, 2 SD kart üzerinde çalışmıyor, sadece vold.conf'da listelenen
3c71

1
Galaxy S3 için vold ve mount dosyalarını gönderebilir misiniz? Kodu kapsayacak şekilde değiştireceğim.
Baron

Galaxy S, bulunan tüm yollar yazılabilir değildi, garip. İki depolama alanı bulundu, varsayılan / mnt / sdcard ve / storage / sdcard0, ikisi de test edemedi
beşinci

1
Mounts dosyasını görmezden gelmek için kodu tweaked. Motorola ve Samsung cihazlarında sorun buydu. Bağlar dosyası external_sd durumunu kapsamıyor, ancak vold olarak listeleniyor. Sınıfımın ilk sürümü, her ikisi için ortak olmayan vold ve atılan öğelerle karşılaştırıldı. Güncellenmiş sınıfı yukarıdaki aynı bağlantıdan alın.
Baron

1
Teşekkürler Baron, bu "cevap" cevabı; en azından yararlı olanı.
pstoppani

23

ContextCompat.getExternalFilesDirs () adlı destek kitaplığı işlevini kullanmayı deneyebilirsiniz :

      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());

Birincisi birincil harici depolama birimi ve geri kalanının gerçek SD kart yolları olması gerekiyor.

Birden çok ".getParentFile ()" nedeni başka bir klasör yukarı gitmektir, çünkü orijinal yol

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

EDIT: sd-kartları yollarını elde etmek için oluşturduğum daha kapsamlı bir yol:

  /**
   * 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;
      }
    }

Çok iyi bir cevap gibi görünüyor, ama kişi bunu basit bir aktiviteye nasıl entegre eder? Tanımlanmamış birçok değişkenler gibi vardır App, ContextCompact,EnvironmentCompact
Antonio

@Antonio ContextCompact, EnvironmentCompact destek kitaplığı aracılığıyla edinilebilir. "App.global ()", her yerde bir Context parametresi eklemeyi sevmediğim için global olarak ayarladığım uygulama bağlamıdır.
android geliştirici

1
Harika! Mayın cihazı v4.4 için çalışır Samsung GT S Advance, diğerleri için çalışacağını umuyoruz
user25

@androiddeveloper Düzenlenen cevap tüm Cihazlar ve SD Kart Boyutları için işe yarar mı?
Rahulrr2602

1
Bu benim için mükemmel çalıştı - kabul edilen cevap olmalı.
Paradoks

17

Tüm Harici Depolama Birimlerini almak için (ister SD kart ister dahili çıkarılamayan depolama olsun ), aşağıdaki kodu kullanabilirsiniz:

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");
            }
        }
    }
}

Alternatif bir uygulama yapabilirsiniz System.getenv ( "EXTERNAL_STORAGE") (örn birincil Harici Depolama dizini almak için "/ depolama / sdcard0" ) ve System.getenv ( "SECONDARY_STORAGE") örneğin (tüm ikincil dizinlerin listesini retieve için " / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ). Bu durumda da, USB sürücülerini hariç tutmak için ikincil dizinlerin listesine filtre uygulamak isteyebileceğinizi unutmayın.

Her durumda, sabit kodlu yolların kullanılmasının her zaman kötü bir yaklaşım olduğunu lütfen unutmayın (özellikle her üretici bunu memnun olarak değiştirebiliyorsa).


2
Sadece bir trol yorum bırakmayan herhangi bir downvoter düşünün, bu yüzden telafi etmek için kaldırdı. ;) AMA, sanırım yönteminiz oldukça keyfi: bu "USB sürücüleri" atlamak ama diğer her şeyi tutmak gerçekten "sdcards" eşit soruyu nasıl biliyoruz? Ayrıca önerileriniz System.getenv("SECONDARY_STORAGE")belgelenmemiş gibi bazı referanslarla da yapabilir.
Sz.

1
Bildiğim kadarıyla, Android API'da tüm Harici Depoları almak için standart bir yönteme referans yok. Bununla birlikte, önerilen yöntem hiç de keyfi değildir. Android'de, her Unix / Linux sisteminde olduğu gibi, TÜM montaj depolama cihazları ortak bir dizinde saklanır / bağlanır: "/ mnt" (depolama cihazlarını monte etmek için standart Unix / Linux dizini) veya en yeni sürümlerde "/ depolama". Bu nedenle, bu klasöre bağlı tüm SD kartları bulacağınızdan emin olabilirsiniz.
Paolo Rovelli

1
System.getenv ("EXTERNAL_STORAGE") yöntemi ile ilgili olarak, API sayfası (çok fazla açıklamıyor) yerine herhangi bir referansım yok: developer.android.com/reference/java/lang/… Ben bulamadım Android sistem ortam değişkenleri için resmi sayfa. Ancak, burada kısa bir liste bulabilirsiniz: herongyang.com/Android/…
Paolo Rovelli

Sdcards hakkında emin değilim demek /mnt, sadece SD kartlar ve USB sürücüler değil, çeşitli diğer fs ağaçları da olabilir olmasıdır. Kodunuz, doğru anladıysam dahili (belki de sanal) dosya sistemi bağlarını da listelerken, soru sadece sdcards ister .
Sz.

1
Anlıyorum. Evet haklısın. Benim yöntemimle dahili (çıkarılamaz) SD bellekleri de alacaksınız.
Paolo Rovelli

16

Richard gibi ben de kullanılabilir depolama seçeneklerinin listesini almak için / proc / mounts dosyası kullanıyorum

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;
    }    
}

Teşekkürler. Mükemmel çalıştı. Ve StorageInfo'yu değiştirilemez hale getirme şeklini seviyorum. Öte yandan printStackTrace? Ne zaman var android.util.Log.e?
Martin

1
Nexus 5 ve Nexus 7'de OTG kablosuyla takılan USB cihazları için
ÇALIŞMADI

1
Bunu SDCard'da dosya yazmak için kullanamıyorum
Eu Vid

@EuVid ile aynı sorun VM / AVD'de çalışıyor ancak donanımda çalışmıyor
casus

uygulanabilir durumum harici SDCard yoluna aşağıdaki gibi bir değişiklik eklemektir: path = path.replace ("mnt / media_rw", "storage");
cwc

11

Herhangi bir ek SD kartın nereye monte edildiğini /proc/mounts(standart Linux dosyası) okuyarak ve vold verisine ( /system/etc/vold.conf) karşı çapraz kontrol yaparak bulmak mümkündür . Ayrıca, döndürülen konumun Environment.getExternalStorageDirectory()vold yapılandırmasında görünmeyebileceğini unutmayın (bazı cihazlarda dahili depolama biriminin bağlantısı kesilemez), ancak yine de listeye dahil edilmesi gerekir. Ancak , bunları kullanıcıya açıklamak için iyi bir yol bulamadık .


Imo, kullanımı dosya sistemini mountokumaktan daha uyumlu /proc. Sorun, SD kartın FAT olarak biçimlendirilmesinin gerekli olmamasıdır. Ayrıca, kart montaj noktası ROM'dan ROM'a değişebilir. Ayrıca, birkaç başka VFAT bölümleri olabilir ...
borisstr 17:11

1
@borisstr: Hm, aslında Android vold kullanıyor , bu yüzden yapılandırmasına bakmak da uygun.
Jan Hudec

Yukarıdaki yazımdan paylaştığım kod dosyası, kullanıcıya keşfedilen kök yollarını tanımlamak için bir yöntem içeriyor. Bak () SetProperties yöntemi.
Baron

1
@borisstr, aslında, hayır, okuma / proc / mount'lar, mountözellikle yürütülebilir dosyaları başlatmaktan kaçınılması nedeniyle, Android cihazlarda yürütülebilir dosyayı başlatmaktan daha taşınabilir .
Chris Stratton

7

Bu konudaki tüm çözümleri bu kez deniyorum. Ancak hepsi bir harici (çıkarılabilir) ve bir dahili (çıkarılamaz) kartı olan cihazlarda düzgün çalışmadı. Harici kartın yolu mümkün değil 'mount' komutundan, 'proc / mounts' dosyasından vb.

Ve kendi çözümümü yaratıyorum (Paulo Luan'da):

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();

6

Kaynak koduna bakarsanız, android.os.EnvironmentAndroid'in yollar için ortam değişkenlerine büyük ölçüde bağlı olduğunu göreceksiniz. Çıkarılabilir sd kartın yolunu bulmak için "SECONDARY_STORAGE" ortam değişkenini kullanabilirsiniz.

/**
 * 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;
}

Örnek kullanım:

public static final File REMOVABLE_STORAGE = getDirectory("SECONDARY_STORAGE");

5

Sadece bunu kullanın:

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)

Bazı cihazlarda SECONDARY_STORAGEiki nokta üst üste (":") ile ayrılmış birkaç yol vardır. Bu yüzden String'i bölüyorum (yukarıdaki cevabıma bakın).
Jared Rummler

Her ikisi de benim için boş.
Tim Cooper

5

Harici bir SD kartın yerini bulmanın evrensel bir yolu var mı?

By evrensel bir şekilde , sen resmi bir şekilde demek eğer; Evet bir tane var.

API seviyesi 19'da, yani Android 4.4 Kitkat sürümünde File[] getExternalFilesDirs (String type),Context mikro SD kartlar veri depolamak / dosyalara uygulamalar izin verdiğini Class.

Android 4.4, uygulamaların depolama için SD kartları kullanmasına izin veren platformun ilk sürümüdür. SD kartlarına API düzeyi 19'dan önceki her türlü erişim özel, desteklenmeyen API'lerle yapıldı.

getExternalFilesDirs (Dize türü) , tüm paylaşılan / harici depolama aygıtlarındaki uygulamaya özgü dizinlere mutlak yollar döndürür. Bu, hem dahili hem de harici belleğe yollar döndüreceği anlamına gelir. Genel olarak, döndürülen ikinci yol, microSD kart (varsa) için depolama yolu olacaktır.

Ama unutmayın,

Çıkarılabilir medya kullanıcı tarafından çıkarılabileceğinden, paylaşılan depolama alanı her zaman mevcut olmayabilir. Medya durumu kullanılarak kontrol edilebilir getExternalStorageState(File).

Bu dosyalarla uygulanan bir güvenlik yoktur. Örneğin, herhangi bir uygulama sahibi WRITE_EXTERNAL_STORAGEbu dosyalara yazabilir.

Google / resmi Android dokümanlarına göre Dahili ve Harici Depolama terminolojisi , düşündüğümüzden oldukça farklıdır .


"Google / resmi Android dokümanlarına göre Dahili ve Harici Depolama terminolojisi düşündüğümüzden oldukça farklı." Evet, aslında sorunun başlığı OP'nin çıkarılabilir bir SD kartını sorduğunu açıklıyor . getExternalFilesDirs()genellikle çıkarılabilir olmayan SD kartları döndürür, bu nedenle hayır, bu çıkarılabilir bir SD kartın yerini bulmanın evrensel bir yolu değildir.
LarsH

"getExternalFilesDirs (String tipi), tüm paylaşılan / harici depolama cihazlarındaki uygulamaya özel dizinlere mutlak yollar döndürür. Bu, hem dahili hem de harici belleğe yollar döndüreceği anlamına gelir." Bu cümle çifti çok yanıltıcıdır, çünkü her ikisinin de doğru olması için "dış" iki farklı ve çatışan şey ifade etmek zorundadır.
LarsH

4

Harici kartı bulmak için kullandığım yol. Montaj cmd dönüşünü kullanın ve ardından vfat parçasını ayrıştırın.

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.
        }
    }
}
}

4

Bu çözüm, System.getenv("SECONDARY_STORAGE") , Marshmallow ile hiç kullanılmadığı .

Test edildi ve üzerinde çalışıyor:

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

    /**
     * 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;
    }

2

Yukarıdaki orijinal cevabımdan beri, vold tarama çeşitli üreticiler arasında artık geçerli değil.

Daha güvenilir ve basit bir yöntem geliştirdim.

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);
    }
});

kökler, usb bağlantılı herhangi bir usb aygıtı dahil olmak üzere sistemdeki tüm yazılabilir kök dizinleri içerecektir.

NOT: canWrite yöntemi android.permission.WRITE_EXTERNAL_STORAGE iznine ihtiyaç duyar.


İsSymlink (File) yöntemi, yeni FileFilter () {} türü için tanımlanmamıştır
Omid Omidi

Herhangi bir fikir bu canWrite nedeniyle Android 4.4 harici sd kartları bulmak başarısız olur?
Anthony

Bu kesinlikle diğer yönteminizden daha basittir, ancak güvenilir midir? Örneğin, bazı Samsung cihazlarında /external_sdharici microSD kart olduğunu okudum ; bazı LG'lerde /_ExternalSD; bazı cihazlarda /sdcard. Belki ikincisi /storage/sdcard0veya benzeri bir semboliktir , ancak bu diğerleri gerçekten güvenilir bir şekilde /storage/*ve tarafından kapsanacak /mount/*mı?
LarsH

Ayrıca, pathname.canWrite()WRITE_EXTERNAL_STORAGE iznini kullanmak ve kullanmak gerekli mi? Neden sadece aramıyorsun pathname.canRead()?
LarsH

1

çok geç oldu ama nihayet ben Android 2.2 + üzerinde çalışan cihazların çoğu (üretici ve android sürümleri tarafından) test ettik bir şey var. çalışmadığını görürseniz, cihaz adınızla yorum yapın. Düzelteceğim. ilgilenen varsa ben nasıl çalıştığını açıklayacağım.

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;
        }

    }

}

Hey downvoter önce deneyin. Eğer çalışmıyorsa cihazınıza yorum yapın. Android 2.2+ ile binden fazla cihazı kullanıyoruz
Ajeet47

Sınıfınız Samsung Galaxy S4, GT-I9500, Android 5.0.1'de (cihaz köklü DEĞİL) bana / mnt / media_rw / extSdCard veriyor. Ancak ES File Manager ile / mnt / media_rw klasöründe görünür bir şey yok ...
isabsent

@isabsent use if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT) {Dosya [] dosya = context.getExternalFilesDirs (null); dönüş dosyası.length> 1? dosya [1]: null; }
Ajeet47

Stackoverflow.com/a/27197248/753575 hakkında ne düşünüyorsun ? Bu yaklaşım Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT için daha kapsamlı mı?
isabsent

Evet. context.getExternalFilesDirs (null) 'dan anlamlı bir yol almanız garanti edilir, ancak kök yolu için kırpmanız gerekir (uygulama dizininiz için yolu döndürür. "/ Android" üzerinde kırpın)
Ajeet47

1

Aşağıdaki kodu yazarak konumu elde edersiniz:

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

uygulama verilerinizi sd_card içindeki / android / data konumunda saklar.

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

list[1]+"/fol" 

dahili için 0 ve sdcard için dosya dizisine 1 geçişi almak için.

Bu kodu bir moto g4 plus ve Samsung cihazında test ettim (hepsi iyi çalışıyor).

umarım bu yardımcı olabilir.


bazen sd kart yolu dizin 1 üzerinde değil, ben 0. endeksinde olduğu durumlarda gördüm. Başka bir şey takip etmek daha iyi
Raghav Satyadev

1

Çıkarılabilir bir SD kart bulmak için kullandığım yöntem . Karmaşık ve muhtemelen bazı durumlar için aşırıya kaçıyor, ancak son birkaç yıldır test ettiğim çok çeşitli Android sürümlerinde ve cihaz üreticilerinde çalışıyor. Takılıysa SD kartını bulamadığı API seviye 15'ten beri herhangi bir cihaz bilmiyorum. Çoğu durumda yanlış pozitif döndürmez, özellikle de aranacak bilinen bir dosyanın adını verirseniz.

Çalışmadığı herhangi bir durumda karşılaşırsanız lütfen bize bildirin.

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

  • <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />Manifest'te unutma . 23 ve daha yüksek API düzeylerinde checkSelfPermission/ kullandığınızdan emin olun.requestPermissions .
  • SD kartta bulmayı umduğunuz bir dosya veya klasör varsa KNOWNFILE = "myappfile" olarak ayarlayın. Bu, algılamayı daha doğru hale getirir.
  • Açıkçası, findSdCardPath(),her ihtiyacınız olduğunda yeniden hesaplamak yerine değerini önbelleğe almak isteyeceksiniz .
  • Log.d()Yukarıdaki kodda bir sürü logging ( ) vardır. Doğru yolun bulunmadığı durumların teşhis edilmesine yardımcı olur. Giriş yapmak istemiyorsanız yorum yapın.

Downvoters, bu cevabın iyileştirilmesi için bir yol önerebilir misiniz?
LarsH

1

Bulduğum tek çalışma çözümü , yansımayı kullanan çözümdü

 /**
 * 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;
}

Ben şahsen yansıma kullanmayı tercih etmiyorum çünkü google android'in yeni sürümlerinde geriye dönük uyumluluğu takdir etmiyor!
Behrouz.M

0

Neden bilmiyorum ama kullanmadan önce genel depolama dizinlerinde oluşturulan bir dosya .createNewFile () çağırmak gerekir. Bu çerçevede, bu yöntemin yorumları bunun yararlı olmadığını söylüyor. İşte bir örnek ...


 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());
        }


0

Bir SD kartın cihazda mevcut olup olmadığını kontrol etmek ve varsa SD kart yolunu almak için bir utils yöntemi oluşturdum.

İhtiyacınız olan 2 yöntemi projenizin sınıfına kopyalayabilirsiniz. Bu kadar.

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;
}

-1

Tüm harici cihazlar için çalışması, Ancak sadece harici cihaz klasör adını aldığınızdan emin olun ve daha sonra File sınıfını kullanarak belirli bir konumdan dosya almanız gerekir.

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;
    }

Arayan:

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());

                }
            }
        }

Harici depolamaya erişim

Harici depolama alanındaki dosyaları okumak veya yazmak için, uygulamanızın READ_EXTERNAL_STORAGE veya WRITE_EXTERNAL_STORAGE sistem izinlerini alması gerekir . Örneğin:

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

-2

/ sdcard => Dahili Depolama (Bir simge bağlantısıdır, ancak çalışmalıdır)

/ mnt / extSdCard => Harici Sdcard

Bu Samsung Galaxy S3 için

Muhtemelen bu çoğu için doğru olabilir ... ancak çift kontrol!


8
Yarısı Samsung olmak üzere birkaç farklı Android telefonum vardı ve bu konumun kullanıldığını hiç görmedim. S3'te doğru olabilir, ancak "büyük olasılıkla bunun çoğu için doğru olduğundan emin olabilirsiniz" demek tamamen yanlıştır.
Geobits

yanlış. /sdcardbenim sony 2305 üzerinde harici bir symlink olduğunu.
jiggunjer

2
Olmayabileceğini söylemedim mi?
robbyoconnor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.