ค้นหาตำแหน่งของการ์ด SD แบบถอดได้


204

มีวิธีสากลในการค้นหาตำแหน่งของการ์ด SD ภายนอกหรือไม่

กรุณาอย่าสับสนกับการจัดเก็บข้อมูลภายนอกจัดเก็บข้อมูลภายนอก

Environment.getExternalStorageState()ส่งคืนพา ธ ไปยังจุดเมานต์ SD ภายในเช่น "/ mnt / sdcard" แต่คำถามนั้นเกี่ยวกับ SD ภายนอก ฉันจะได้รับเส้นทางเช่น "/ mnt / sdcard / external_sd" ได้อย่างไร (อาจแตกต่างกันไปตามอุปกรณ์)

ฉันเดาว่าฉันจะจบด้วยการกรองผลลัพธ์ของmountคำสั่งด้วยชื่อระบบไฟล์ แต่ฉันไม่แน่ใจว่าวิธีนี้จะแข็งแกร่งพอ


นี่คือทางออกของฉันที่ทำงานจนถึง Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

'Environment.getExternalStorageState () ส่งคืนพา ธ ไปยังจุดเมานต์ SD ภายในเช่น "/ mnt / sdcard"' ไม่ใช่ในแง่ที่ Android ใช้คำว่า ฉันเชื่อว่าคำที่คุณต้องการคือ "ไม่สามารถถอดออกได้"
LarsH

คำตอบ:


162

Environment.getExternalStorageState() ส่งคืนพา ธ ไปยังจุดเมานต์ SD ภายในเช่น "/ mnt / sdcard"

ไม่Environment.getExternalStorageDirectory()หมายถึงสิ่งที่ผู้ผลิตอุปกรณ์ถือเป็น "ที่จัดเก็บข้อมูลภายนอก" ในอุปกรณ์บางตัวนี่เป็นสื่อที่ถอดออกได้เช่นการ์ด SD ในบางอุปกรณ์นี่เป็นส่วนหนึ่งของแฟลชในอุปกรณ์ ที่นี่ "ที่เก็บข้อมูลภายนอก" หมายถึง "สิ่งที่สามารถเข้าถึงได้ผ่านโหมด USB Mass Storage เมื่อติดตั้งบนเครื่องโฮสต์" อย่างน้อยสำหรับ Android 1.x และ 2.x

แต่คำถามนั้นเกี่ยวกับ SD ภายนอก วิธีรับพา ธ เช่น "/ mnt / sdcard / external_sd" (อาจแตกต่างกันไปตามอุปกรณ์)

Android ไม่มีแนวคิดของ "SD ภายนอก" นอกเหนือจากที่จัดเก็บข้อมูลภายนอกตามที่อธิบายไว้ข้างต้น

หากผู้ผลิตอุปกรณ์เลือกที่จะให้มีที่เก็บข้อมูลภายนอกเป็นแฟลชออนบอร์ดและมีการ์ด SD คุณจะต้องติดต่อผู้ผลิตรายนั้นเพื่อพิจารณาว่าคุณสามารถใช้การ์ด SD ได้หรือไม่ (ไม่รับประกัน) ใช้มันเช่นพา ธ ที่จะใช้


UPDATE

สองสิ่งที่ควรทราบเมื่อเร็ว ๆ นี้:

ครั้งแรกบน Android 4.4 ขึ้นไป, คุณไม่ได้มีการเขียนการเข้าถึงสื่อที่ถอดได้ (เช่น "ภายนอก SD") ยกเว้นสถานที่ใด ๆ ในสื่อที่อาจจะมีการส่งกลับโดยและgetExternalFilesDirs() getExternalCacheDirs()ดูการวิเคราะห์ที่ยอดเยี่ยมของ Dave Smithโดยเฉพาะอย่างยิ่งหากคุณต้องการรายละเอียดในระดับต่ำ

ประการที่สองเพื่อมิให้คนอื่นเล่นลิ้นว่าการเข้าถึงสื่อที่ถอดได้นั้นเป็นส่วนหนึ่งของ Android SDK หรือไม่นี่คือการประเมินของ Dianne Hackborn :

... โปรดทราบ: จนกว่า Android 4.4 แพลตฟอร์ม Android อย่างเป็นทางการจะไม่รองรับการ์ด SD เลยยกเว้นสองกรณีพิเศษ: รูปแบบการจัดเก็บข้อมูลโรงเรียนเก่าที่จัดเก็บข้อมูลภายนอกเป็นการ์ด SD (ซึ่งยังคงรองรับแพลตฟอร์มในปัจจุบัน) และฟีเจอร์ขนาดเล็กที่เพิ่มลงใน Android 3.0 ซึ่งจะสแกนการ์ด SD เพิ่มเติมและเพิ่มไปยังผู้ให้บริการสื่อและให้แอปเข้าถึงไฟล์ของพวกเขาแบบอ่านอย่างเดียว (ซึ่งยังรองรับในแพลตฟอร์มปัจจุบัน)

Android 4.4 เป็นแพลตฟอร์มแรกที่อนุญาตให้แอปพลิเคชันใช้การ์ด SD สำหรับการจัดเก็บจริง ๆ การเข้าถึงใด ๆ ก่อนที่จะผ่าน API ส่วนตัวที่ไม่ได้รับการสนับสนุน ตอนนี้เรามี API ที่ค่อนข้างสมบูรณ์ในแพลตฟอร์มที่อนุญาตให้แอปพลิเคชันใช้การ์ด SD ในวิธีที่สนับสนุนได้ดีกว่าที่เคยมีมาก่อน: พวกเขาสามารถใช้พื้นที่เก็บข้อมูลเฉพาะแอปได้ฟรีโดยไม่ต้องใช้ใด ๆ การอนุญาตในแอปและสามารถเข้าถึงไฟล์อื่น ๆ บนการ์ด SD ตราบใดที่พวกเขาผ่านตัวเลือกไฟล์อีกครั้งโดยไม่ต้องมีการอนุญาตพิเศษใด ๆ


4
และปัญหานี้กำลังทวีความสำคัญมากขึ้นเนื่องจากอุปกรณ์ HC และ ICS ออกมาในจุดนั้นว่า ด้านบนมันปิดที่ผู้ใช้ส่วนใหญ่มีเงื่อนงำอย่างไม่มีวิธีการค้นหาที่ sdcard ของพวกเขาอยู่ในระบบแฟ้ม
Tony Maro

283
ดังนั้นคำตอบของคุณคือติดต่อผู้ผลิต ไม่มีประโยชน์.
dragonroot

6
ส่วนสุดท้ายของคำตอบนั้นไม่แม่นยำนัก - เป็นไปได้ที่จะตรวจจับเส้นทาง SD การ์ดโดยทำตามคำตอบด้านล่างนี้ (scan / proc / mounts, /system/etc/vold.fstab ฯลฯ )
เรียนรู้ OpenGL ES

8
@CommonsWare: อย่างไรก็ตามมันยังไม่ถูกต้องที่ "ต้องการ" เพื่อติดต่อผู้ผลิตเมื่อมีวิธีแก้ปัญหาที่ทำงานบนอุปกรณ์จำนวนมากออกมีและนอกจากนั้น SDK เองไม่สอดคล้องกับอุปกรณ์ทั้งหมดดังนั้นจึงไม่มีการรับประกัน แม้ว่าโซลูชันเหล่านี้จะไม่ทำงานบนอุปกรณ์ทั้งหมด แต่ก็ทำงานบนอุปกรณ์เพียงพอที่แอพ Android จำนวนมากในตลาดใช้เทคนิคเหล่านี้เพื่อตรวจหาเส้นทางการ์ด SD ภายนอก ฉันคิดว่ามันค่อนข้างรุนแรงและก่อนวัยอันควรที่จะเรียกนักพัฒนาเหล่านี้ว่าโง่เขลา - ลูกค้าไม่ได้เป็นผู้ตัดสินขั้นสุดท้ายอย่างแน่นอนหรือ
เรียนรู้ OpenGL ES

5
@CommonsWare นั่นยุติธรรมพอแล้ว ฉันเห็นด้วยกับคุณอย่างแน่นอนว่านักพัฒนาซอฟต์แวร์ไม่สามารถสันนิษฐานได้ว่านี่จะทำงานได้ทุกที่และรหัสดังกล่าวไม่สามารถรับประกันได้ว่าจะทำงานบนอุปกรณ์ทั้งหมดหรือสำหรับ Android ทุกรุ่น หวังว่ามันจะได้รับการแก้ไขใน SDK! ในขณะเดียวกันยังมีตัวเลือกที่ทำงานบนอุปกรณ์หลายอย่างและสามารถปรับปรุงประสบการณ์การใช้งานของผู้ใช้และได้รับตัวเลือกระหว่างความสำเร็จ 80% และความสำเร็จ 0% ฉันจะใช้ 80%
เรียนรู้ OpenGL ES

64

ฉันคิดวิธีแก้ปัญหาต่อไปนี้ตามคำตอบที่พบที่นี่

รหัส:

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

การใช้:

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 =)
Richard

2
สวัสดีอีกครั้งริชาร์ด - เชื่อหรือไม่ฉันต้องถามว่า: คุณลองเขียนและอ่านไฟล์ด้วยวิธีนี้จริง ๆ ไม่ใช่แค่เอามาเป็น dirs หรือเปล่า? จำปัญหา "/ sdcard0" เก่าของเราได้หรือไม่ ฉันลองใช้รหัสนี้และล้มเหลวใน S3 เมื่อฉันพยายามอ่านกลับในไฟล์ที่เขียนไว้ ... นี่มันแปลกมาก ... และเจ็บปวด :))
Howard Pautz

10
สิ่งนี้ล้มเหลวบนอุปกรณ์ที่ไม่มีการ์ด SD 2 การ์ด มันจะถือว่าพบครั้งที่ 1 ว่าเป็นภายในและที่สองพบว่าเป็นภายนอก ...
Caner

ไม่สามารถใช้งานได้กับอุปกรณ์ USB ที่ต่อผ่านสายเคเบิล OTG ใน Nexus 5 และ Nexus 7
Khawar Raza

4
/system/etc/vold.fstab ไม่สามารถเข้าถึงได้ใน android 4.3+
Ali

37

ฉันมีแอปพลิเคชั่นที่ใช้ListPreferenceสถานที่ซึ่งผู้ใช้จำเป็นต้องเลือกตำแหน่งที่ต้องการบันทึกบางสิ่ง

ในแอพนั้นฉันสแกน/proc/mountsและ/system/etc/vold.fstabหาจุดต่อ sdcard ผมเก็บไว้ติดคะแนนจากแต่ละไฟล์ออกเป็นสองแยกต่างหากArrayLists

จากนั้นฉันเปรียบเทียบรายการหนึ่งกับรายการอื่นและรายการที่ถูกทิ้งซึ่งไม่ได้อยู่ในทั้งสองรายการ นั่นทำให้ฉันมีรายการของรูทพา ธ ไปยัง sdcard แต่ละอัน

จากนั้นผมทดสอบเส้นทางด้วยFile.exists(), และFile.isDirectory() File.canWrite()หากการทดสอบใด ๆ เหล่านั้นเป็นเท็จฉันจะละทิ้งเส้นทางนั้นจากรายการ

อะไรก็ตามที่อยู่ในรายการฉันจะแปลงเป็นString[]อาร์เรย์เพื่อให้สามารถใช้ListPreferenceแอตทริบิวต์ attribute

คุณสามารถดูรหัสได้ที่นี่: http://sapienmobile.com/?p=204


ในปีนี้มันใช้งานไม่ได้กับ Galaxy S3, การ์ด SD 2 ใบเพียงอันเดียวที่ระบุใน vold.conf
3c71

1
@ 3c71 - คุณสามารถส่งไฟล์ vold และ mounts ให้กับ Galaxy S3 ได้ไหม ฉันจะปรับแต่งรหัสเพื่อครอบคลุม
บารอน

Galaxy S เส้นทางทั้งหมดที่พบไม่สามารถเขียนได้แปลก พบที่เก็บข้อมูลสองแห่งเริ่มต้น / mnt / sdcard และ / storage / sdcard0 ทั้งสองไม่สามารถทดสอบได้
ที่ห้า

1
ฉัน tweaked รหัสเพื่อละเว้นการเมานท์ไฟล์ นั่นคือปัญหาของอุปกรณ์ Motorola และ Samsung ไฟล์การเมาต์ไม่ครอบคลุมเคส external_sd แต่ไฟล์ถูกแสดงเป็น vold รุ่นเริ่มต้นของชั้นเรียนของฉันได้เปรียบเทียบการเมานต์กับรายการ vold และรายการที่ถูกทิ้งซึ่งไม่เป็นเรื่องปกติสำหรับทั้งคู่ หยิบคลาสที่อัพเดทจากลิงค์เดียวกันด้านบน
บารอน

1
ขอบคุณบารอนนี่คือคำตอบ "" อย่างน้อยก็มีประโยชน์
pstoppani

23

คุณสามารถลองใช้ฟังก์ชันไลบรารีการสนับสนุนที่เรียกว่าContextCompat.getExternalFilesDirs () :

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

อันแรกเป็นที่เก็บข้อมูลภายนอกหลักและส่วนที่เหลือควรเป็นเส้นทางการ์ด SD จริง

เหตุผลสำหรับหลาย ๆ ".getParentFile ()" คือการขึ้นไปอีกโฟลเดอร์หนึ่งเนื่องจากเส้นทางเดิมคือ

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

แก้ไข: นี่เป็นวิธีที่ครอบคลุมมากขึ้นที่ฉันได้สร้างขึ้นเพื่อรับเส้นทางการ์ด sd:

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

ดูเหมือนจะเป็นคำตอบที่ดีมาก แต่จะรวมสิ่งนี้เข้ากับกิจกรรมง่ายๆได้อย่างไร มีหลายตัวแปรที่ไม่ได้กำหนดไว้เช่นมีApp, ContextCompact,EnvironmentCompact
อันโตนิโอ

@Antonio ContextCompact, EnvironmentCompact มีให้บริการผ่านห้องสมุดสนับสนุน "App.global ()" เป็นเพียงบริบทของแอปพลิเคชันซึ่งฉันตั้งไว้ทั่วโลกเนื่องจากฉันไม่ต้องการเพิ่มพารามิเตอร์บริบททุกที่
นักพัฒนา android

1
ที่ดี! ใช้งานได้กับอุปกรณ์เหมือง v4.4 Samsung GT S Advance หวังว่าจะใช้งานได้กับผู้อื่น
25

@androiddeveloper คำตอบที่แก้ไขแล้วจะใช้ได้กับอุปกรณ์และขนาดการ์ด SD ทั้งหมดหรือไม่
Rahulrr2602

1
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน - ควรเป็นคำตอบที่ยอมรับได้
Paradox

17

เพื่อดึงข้อมูลที่เก็บข้อมูลภายนอกทั้งหมด(ไม่ว่าจะเป็นการ์ด SDหรือที่เก็บข้อมูลภายในแบบถอดไม่ได้ ) คุณสามารถใช้รหัสต่อไปนี้:

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

หรือคุณอาจใช้System.getenv ("EXTERNAL_STORAGE")เพื่อเรียกข้อมูลไดเรกทอรีจัดเก็บข้อมูลภายนอกหลัก (เช่น"/ storage / sdcard0" ) และSystem.getenv ("SECONDARY_STORAGE")เพื่อรับรายการไดเรกทอรีรองทั้งหมด (เช่น" / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ) โปรดจำไว้ว่าในกรณีนี้คุณอาจต้องการกรองรายชื่อของไดเรกทอรีที่สองเพื่อไม่ให้รวมไดรฟ์ USB

ไม่ว่าในกรณีใด ๆ โปรดทราบว่าการใช้เส้นทางแบบฮาร์ดโค้ดเป็นวิธีการที่ไม่ดีเสมอ (โดยเฉพาะอย่างยิ่งเมื่อผู้ผลิตทุกรายสามารถเปลี่ยนได้ตามความพอใจ)


2
เพียงแค่พิจารณาผู้ลงคะแนนเสียงคนใดก็ตามที่ไม่ได้แสดงความคิดเห็นใด ๆ โทรลล์ดังนั้นฉันจึงโหวตขึ้นเพื่อชดเชย ;) แต่ฉันคิดว่าวิธีการของคุณนั้นค่อนข้างไม่มีกฎเกณฑ์: เราจะรู้ได้อย่างไรว่าการข้าม "ไดรฟ์ USB" เหล่านั้น แต่การรักษาทั้งหมดอื่น ๆ นั้นเท่ากับ "sdcard" ตามที่ถามในคำถาม นอกจากนี้ข้อเสนอแนะของคุณSystem.getenv("SECONDARY_STORAGE")ยังสามารถทำได้กับการอ้างอิงบางอย่างเนื่องจากดูเหมือนว่าไม่มีเอกสาร
Sz.

1
เท่าที่ฉันรู้ใน Android API ไม่มีการอ้างอิงถึงวิธีการมาตรฐานในการดึงที่เก็บข้อมูลภายนอกทั้งหมด อย่างไรก็ตามวิธีการที่นำเสนอไม่ได้เป็นเรื่องที่เด็ดขาด ใน Android เช่นเดียวกับในทุกระบบ Unix / Linux อุปกรณ์จัดเก็บข้อมูลการติดตั้งทั้งหมดจะถูกจัดเก็บ / เชื่อมโยงในไดเรกทอรีทั่วไป: "/ mnt" (ไดเรกทอรี Unix / Linux มาตรฐานสำหรับการติดตั้งอุปกรณ์จัดเก็บข้อมูล) หรือในรุ่นล่าสุด "/ การจัดเก็บข้อมูล" นั่นคือเหตุผลที่คุณมั่นใจได้เลยว่าคุณจะพบการ์ด SD ทั้งหมดที่เชื่อมโยงอยู่ในโฟลเดอร์นี้
เปาโล Rovelli

1
เกี่ยวกับวิธีการ System.getenv ("EXTERNAL_STORAGE") ฉันไม่มีการอ้างอิงใด ๆ มากกว่าหน้า API (ซึ่งไม่อธิบายมาก): developer.android.com/reference/java/lang/…ฉันไม่พบสิ่งใดเลย หน้าอย่างเป็นทางการสำหรับตัวแปรสภาพแวดล้อมระบบ Android อย่างไรก็ตามที่นี่คุณสามารถค้นหารายการสั้น ๆ ของพวกเขา: herongyang.com/Android/…
Paolo Rovelli

สิ่งที่ฉันหมายถึงโดยไม่แน่ใจเกี่ยวกับ sdcard ก็คือใน/mntนั้นสามารถมีต้นไม้ fs อื่น ๆ อีกมากมายเช่นกันไม่ใช่แค่การ์ด SD และไดรฟ์ USB รหัสของคุณก็จะแสดงรายการภายใน (บางทีก็เสมือน) เมาท์ระบบไฟล์ใด ๆ หากผมเข้าใจมันขวาขณะที่คำถามที่ต้องการ sdcards เท่านั้น
Sz.

1
ฉันเห็น. ใช่คุณถูก. ด้วยวิธีการของฉันคุณจะสามารถเรียกคืนความทรงจำ SD ภายใน (ไม่สามารถถอดออกได้)
เปาโล Rovelli

15

เช่นเดียวกับริชาร์ดฉันยังใช้ไฟล์/ proc / mountsเพื่อรับรายการตัวเลือกพื้นที่เก็บข้อมูลที่มีอยู่

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

ขอขอบคุณ ทำงานได้อย่างสมบูรณ์แบบ และฉันชอบวิธีที่คุณทำให้ StorageInfo ไม่สามารถเปลี่ยนแปลงได้ ในทางกลับกันprintStackTrace? เมื่อเรามีandroid.util.Log.e?
Martin

1
ไม่สามารถใช้งานได้กับอุปกรณ์ USB ที่ต่อผ่านสาย OTG บน Nexus 5 และ Nexus 7
Khawar Raza

1
ฉันไม่สามารถใช้สิ่งนี้เพื่อเขียนไฟล์บน SDCard
Eu Vid

ปัญหาเดียวกับ @EuVid ใช้งานได้กับ VM / AVD แต่ไม่ได้ใช้กับฮาร์ดแวร์
สายลับ

11

เป็นไปได้ที่จะหาที่ติดตั้งการ์ด SD เพิ่มเติมโดยการอ่าน/proc/mounts(ไฟล์ Linux มาตรฐาน) และตรวจสอบข้ามข้อมูลvold ( /system/etc/vold.conf) และโปรดทราบว่าตำแหน่งที่ส่งคืนโดยEnvironment.getExternalStorageDirectory()อาจไม่ปรากฏในการกำหนดค่า vold (ในบางอุปกรณ์เป็นที่เก็บข้อมูลภายในที่ไม่สามารถถอดออกได้) แต่ยังต้องรวมอยู่ในรายการ อย่างไรก็ตามเราไม่พบวิธีที่ดีในการอธิบายให้ผู้ใช้ทราบ


ในทางกลับกันการใช้งานmountนั้นเข้ากันได้ดีกว่าการอ่าน/procระบบไฟล์ ปัญหาคือการ์ด SD ไม่จำเป็นต้องฟอร์แมตเป็น FAT นอกจากนี้จุดยึดการ์ดอาจแตกต่างกันไปในแต่ละ ROM นอกจากนี้ยังอาจจะมีคนอื่น ๆ หลาย VFAT พาร์ทิชัน ...
borisstr

1
@borisstr: อืมจริงๆแล้ว Android ใช้voldดังนั้นเมื่อดูที่ config มันก็เหมาะสมแล้ว
Jan Hudec

ไฟล์รหัสที่ฉันแชร์จากโพสต์ของฉันมีวิธีการอธิบายเส้นทางรากที่ค้นพบไปยังผู้ใช้ ดูที่เมธอดsetProperties ()
บารอน

1
@ Borisstr ที่จริงแล้วไม่มีการอ่าน / proc / mounts เป็นอุปกรณ์พกพาบนอุปกรณ์ Android ได้มากกว่าการเรียกใช้งานmountโปรแกรมที่เรียกใช้
Chris Stratton

7

ฉันลองวิธีแก้ปัญหาทั้งหมดในหัวข้อนี้ในเวลานี้ แต่ทั้งหมดนั้นทำงานไม่ถูกต้องบนอุปกรณ์ที่มีการ์ดภายนอก (ถอดออกได้) และการ์ดภายใน (ไม่สามารถถอดออกได้) หนึ่งการ์ด เส้นทางของการ์ดภายนอกไม่สามารถหาได้จากคำสั่ง 'mount', จากไฟล์ 'proc / mounts' เป็นต้น

และฉันสร้างวิธีการแก้ปัญหาของตัวเอง (ในเปาโล 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();

6

หากคุณดูซอร์สโค้ดandroid.os.Environmentคุณจะเห็นว่า Android อาศัยตัวแปรสภาพแวดล้อมสำหรับเส้นทางอย่างมาก คุณสามารถใช้ตัวแปรสภาพแวดล้อม "SECONDARY_STORAGE" เพื่อค้นหาเส้นทางไปยังการ์ด sd ที่ถอดออกได้

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

ตัวอย่างการใช้งาน:

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

5

เพียงแค่ใช้สิ่งนี้:

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)

ในบางอุปกรณ์SECONDARY_STORAGEมีหลายเส้นทางคั่นด้วยเครื่องหมายโคลอน (":") นี่คือเหตุผลที่ฉันแยกสตริง (ดูคำตอบของฉันด้านบน)
Jared Rummler

ทั้งสองนี้คืนค่าว่างให้ฉัน
Tim Cooper

5

มีวิธีสากลในการค้นหาตำแหน่งของการ์ด SD ภายนอกหรือไม่

โดยวิธีสากลถ้าคุณหมายถึงวิธีที่เป็นทางการ; ใช่มีอยู่

ใน API ระดับ 19 เช่นใน Android เวอร์ชัน 4.4 Kitkat พวกเขาได้เพิ่มFile[] getExternalFilesDirs (String type)ในContextClass ที่อนุญาตให้แอปจัดเก็บข้อมูล / ไฟล์ในการ์ด micro SD

Android 4.4 เป็นแพลตฟอร์มแรกที่อนุญาตให้แอปใช้การ์ด SD สำหรับการจัดเก็บได้จริง การเข้าถึงการ์ด SD ก่อนระดับ API 19 จะผ่าน API ส่วนตัวที่ไม่ได้รับการสนับสนุน

getExternalFilesDirs (ชนิดสตริง)ส่งคืนพา ธ สัมบูรณ์ไปยังไดเร็กทอรีเฉพาะแอปพลิเคชันบนอุปกรณ์เก็บข้อมูลภายนอก / แชร์ทั้งหมด มันหมายความว่ามันจะกลับเส้นทางไปยังหน่วยความจำทั้งภายในและภายนอก โดยทั่วไปเส้นทางที่สองที่ส่งคืนจะเป็นเส้นทางการจัดเก็บข้อมูลสำหรับการ์ด microSD (ถ้ามี)

แต่ทราบว่า

ที่เก็บข้อมูลที่ใช้ร่วมกันอาจไม่สามารถใช้งานได้ตลอดเวลาเนื่องจากผู้ใช้สามารถถอดสื่อที่ถอดออกได้ getExternalStorageState(File)รัฐสื่อสามารถตรวจสอบได้โดยใช้

ไม่มีความปลอดภัยบังคับใช้กับไฟล์เหล่านี้ ตัวอย่างเช่นแอปพลิเคชันใด ๆ ที่ถือWRITE_EXTERNAL_STORAGEสามารถเขียนไฟล์เหล่านี้

คำศัพท์ที่เก็บข้อมูลภายในและภายนอกตาม Google / เอกสาร Android อย่างเป็นทางการค่อนข้างแตกต่างจากที่เราคิด


"คำศัพท์ที่เก็บข้อมูลภายในและภายนอกตาม Google / เอกสาร Android อย่างเป็นทางการค่อนข้างแตกต่างจากที่เราคิด" ใช่ในความเป็นจริงชื่อของคำถามชี้แจงว่า OP กำลังถามเกี่ยวกับการ์ด SD แบบถอดได้ getExternalFilesDirs()มักจะส่งคืนการ์ด SD ที่ไม่สามารถถอดออกได้ดังนั้นไม่ใช่นี่ไม่ใช่วิธีสากลในการค้นหาตำแหน่งของการ์ด SD ที่ถอดออกได้
LarsH

"getExternalFilesDirs (ประเภทสตริง) ส่งคืนพา ธ สัมบูรณ์ไปยังไดเรกทอรีเฉพาะแอปพลิเคชันบนอุปกรณ์เก็บข้อมูลภายนอก / แชร์ทั้งหมดซึ่งหมายความว่าจะส่งคืนพา ธ ไปยังหน่วยความจำทั้งภายในและภายนอก" ประโยคคู่นี้ทำให้เข้าใจผิดมากเพราะเพื่อให้ทั้งคู่เป็นจริงคำว่า "ภายนอก" จะต้องหมายถึงสองสิ่งที่แตกต่างและขัดแย้งกัน
LarsH

4

นี่คือวิธีที่ฉันใช้เพื่อค้นหาการ์ดภายนอก ใช้ Mount cmd return แล้วแยกส่วน vfat

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

โซลูชันนี้จัดการกับความจริงที่System.getenv("SECONDARY_STORAGE")ไม่มีประโยชน์กับ Marshmallow

ผ่านการทดสอบและทำงานกับ:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - หุ้น)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - หุ้น)
  • Samsung Galaxy S4 (Android 4.4 - หุ้น)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - หุ้น)

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

ตั้งแต่คำตอบเดิมของฉันด้านบนการสแกน vold ไม่สามารถใช้งานได้กับผู้ผลิตหลายราย

ฉันได้พัฒนาวิธีการที่น่าเชื่อถือและตรงไปตรงมามากขึ้น

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

รากจะมีไดเรกทอรีรากทั้งหมดที่เขียนได้บนระบบรวมถึงอุปกรณ์ usb ที่เชื่อมต่อด้วย usb

หมายเหตุ: วิธี canWrite ต้องได้รับอนุญาต android.permission.WRITE_EXTERNAL_STORAGE


เมธอด isSymlink (ไฟล์) ไม่ได้กำหนดไว้สำหรับ FileFilter ชนิดใหม่ () {}
Omid Omidi

มีความคิดใดบ้างที่จะไม่สามารถหาการ์ด SD ภายนอกบน Android 4.4 เนื่องจาก canWrite?
Anthony

นี่เป็นวิธีที่ตรงไปตรงมามากกว่าวิธีอื่น ๆ ของคุณ แต่มันน่าเชื่อถือใช่ไหม เช่นฉันได้อ่านแล้วว่าอุปกรณ์ซัมซุงบางรุ่น/external_sdเป็นการ์ด microSD ภายนอก ในบาง LGS ก็/_ExternalSD; /sdcardบนอุปกรณ์บางอย่างมัน บางทีหลังเป็น symlink ไป/storage/sdcard0หรือคล้ายกัน แต่คนอื่น ๆ เหล่านี้จะได้รับการคุ้มครองอย่างน่าเชื่อถือโดย/storage/*และ/mount/*?
LarsH

นอกจากนี้จำเป็นต้องใช้pathname.canWrite()และต้องการสิทธิ์ WRITE_EXTERNAL_STORAGE หรือไม่ ทำไมไม่โทรมาpathname.canRead()?
LarsH

1

มันช้ามาก แต่ในที่สุดฉันก็มีบางอย่างที่ฉันได้ทดสอบกับอุปกรณ์ส่วนใหญ่ หากคุณพบว่าใช้งานไม่ได้ให้แสดงความคิดเห็นด้วยชื่ออุปกรณ์ของคุณ ฉันจะซ่อมมัน. หากใครสนใจฉันจะอธิบายวิธีการใช้งาน

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

    }

}

เฮ้ downvoter โปรดลองก่อนถ้ามันไม่ทำงานแล้วแสดงความคิดเห็นอุปกรณ์ของคุณ เราใช้มันมากกว่าพันอุปกรณ์ที่ใช้ Android 2.2+
Ajeet47

ชั้นเรียนของคุณให้ฉัน / mnt / media_rw / extSdCard ใน Samsung Galaxy S4, GT-I9500, Android 5.0.1 (อุปกรณ์ไม่ได้ถูกรูท) แต่จะไม่มีสิ่งใดปรากฏในโฟลเดอร์ / mnt / media_rw ด้วย ES File Manager ...
isabsent

@ isabsent ใช้ถ้า (Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT) {ไฟล์ [] ไฟล์ = context.getExternalFilesDirs (null); return file.length> 1? file [1]: null; }
Ajeet47

คุณคิดอย่างไรเกี่ยวกับstackoverflow.com/a/27197248/753575 ? วิธีนี้มีความครอบคลุมมากกว่าสำหรับกรณีของ Build.VERSION.SDK_INT> = Build.VERSION_CODES.KITKAT หรือไม่
isabsent

ใช่. มันรับประกันว่าคุณจะได้รับเส้นทางที่มีความหมายจาก context.getExternalFilesDirs (null) แต่คุณได้ตัดมันสำหรับเส้นทางราก (มันจะกลับเส้นทางสำหรับไดเรกทอรีแอปของคุณตัดมันใน "/ Android")
Ajeet47

1

โดยการเขียนรหัสด้านล่างคุณจะได้รับตำแหน่ง:

จัดเก็บ / / 663D-554E / Android / ข้อมูล / app_package_name / / ไฟล์

ซึ่งจัดเก็บข้อมูลแอปของคุณที่ / android / data ตำแหน่งภายใน sd_card

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

list[1]+"/fol" 

สำหรับการรับตำแหน่งผ่าน 0 สำหรับภายในและ 1 สำหรับ sdcard ไปยังอาร์เรย์ไฟล์

ฉันทดสอบโค้ดนี้ใน moto g4 plus และอุปกรณ์ Samsung (ใช้งานได้ดี)

หวังว่านี่อาจเป็นประโยชน์


บางครั้งเส้นทางของการ์ด sd ไม่ได้อยู่ในดัชนี 1 ฉันได้เห็นกรณีที่เป็นดัชนี 0 ดีกว่าที่จะติดตามสิ่งอื่น
Raghav Satyadev

1

นี่คือวิธีที่ฉันใช้เพื่อค้นหาการ์ด SD แบบถอดได้ มันซับซ้อนและอาจ overkill สำหรับบางสถานการณ์ แต่ทำงานได้กับ Android และผู้ผลิตอุปกรณ์ที่ฉันได้ทำการทดสอบเมื่อไม่กี่ปีที่ผ่านมา ฉันไม่ทราบเกี่ยวกับอุปกรณ์ใด ๆ ตั้งแต่ API ระดับ 15 ซึ่งไม่พบการ์ด SD หากมีอุปกรณ์หนึ่งตัว มันจะไม่ส่งคืนผลบวกปลอมในกรณีส่วนใหญ่โดยเฉพาะถ้าคุณให้ชื่อของไฟล์ที่รู้จักเพื่อค้นหา

โปรดแจ้งให้เราทราบหากคุณพบปัญหาใด ๆ ที่ไม่สามารถใช้งานได้

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" />ในรายการ และที่ API ระดับ 23 ขึ้นไปต้องแน่ใจว่าได้ใช้checkSelfPermission/requestPermissions /
  • ตั้งค่า KNOWNFILE = "myappfile" หากมีไฟล์หรือโฟลเดอร์ที่คุณคาดว่าจะพบในการ์ด SD ทำให้การตรวจจับมีความแม่นยำยิ่งขึ้น
  • เห็นได้ชัดว่าคุณต้องการแคชค่าfindSdCardPath(),แทนที่จะทำการคำนวณใหม่ทุกครั้งที่คุณต้องการ
  • มีพวงของการบันทึก ( Log.d()) ในรหัสข้างต้น ช่วยวินิจฉัยกรณีที่ไม่พบเส้นทางที่ถูกต้อง แสดงความคิดเห็นออกหากคุณไม่ต้องการเข้าสู่ระบบ

คุณสามารถแนะนำวิธีปรับปรุงคำตอบนี้ได้หรือไม่?
LarsH

1

ทางออกเดียวที่ฉันพบคือโซลูชันที่ใช้การสะท้อนกลับ

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

ฉันเองไม่ชอบใช้การสะท้อนเพราะ Google ไม่ชอบความเข้ากันได้แบบย้อนหลังใน Android เวอร์ชันใหม่!
Behrouz.M

0

ฉันไม่รู้ว่าทำไม แต่ฉันต้องโทร. .createNewFile () บนไฟล์ที่สร้างในไดเรกทอรีที่เก็บข้อมูลสาธารณะก่อนที่จะใช้ ในกรอบข้อคิดเห็นสำหรับวิธีนั้นบอกว่ามันไม่มีประโยชน์ นี่คือตัวอย่าง ...


 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

ฉันได้สร้างวิธี utils เพื่อตรวจสอบว่ามีการ์ด SD บนอุปกรณ์หรือไม่และรับเส้นทางของการ์ด SD บนอุปกรณ์ถ้ามี

คุณสามารถคัดลอก 2 วิธีต่อไปนี้ลงในคลาสของโครงการที่คุณต้องการ นั่นคือทั้งหมดที่

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

มันใช้งานได้กับอุปกรณ์ภายนอกทั้งหมด แต่ต้องแน่ใจว่าได้รับชื่อโฟลเดอร์ของอุปกรณ์ภายนอกเท่านั้นจากนั้นคุณต้องรับไฟล์จากตำแหน่งที่กำหนดโดยใช้คลาสไฟล์

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

โทรศัพท์:

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

                }
            }
        }

การเข้าถึงที่เก็บข้อมูลภายนอก

ในการอ่านหรือเขียนไฟล์ในที่จัดเก็บข้อมูลภายนอกแอปของคุณจะต้องได้รับอนุญาตระบบREAD_EXTERNAL_STORAGEหรือWRITE_EXTERNAL_STORAGE ตัวอย่างเช่น:

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

-2

/ sdcard => Internal Storage (เป็น symlink แต่ควรใช้งานได้)

/ mnt / extSdCard => External Sdcard

นี่สำหรับ Samsung Galaxy S3

คุณอาจจะเห็นว่าเรื่องนี้เป็นเรื่องจริงสำหรับคนส่วนใหญ่ ...


8
ฉันมีโทรศัพท์ Android หลายรุ่นประมาณครึ่งหนึ่งของซัมซุงและไม่เคยเห็นสถานที่นี้ใช้ มันอาจจะเป็นจริงใน S3 แต่พูดว่า "คุณสามารถธนาคารอาจจะเกี่ยวกับเรื่องนี้เป็นจริงมากที่สุด" คืออย่างสมบูรณ์ที่ไม่ถูกต้อง
Geobits

ไม่ถูกต้อง. /sdcardเป็น symlink กับภายนอกใน sony 2305 ของฉัน
jiggunjer

2
ฉันไม่ได้บอกว่ามันอาจจะไม่ใช่หรือ
robbyoconnor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.