이동식 SD 카드의 위치 찾기 / sdcard”와 같은

외부 SD 카드의 위치를 ​​찾을 수있는 보편적 인 방법이 있습니까?

외부 저장소 와 혼동하지 마십시오 .

Environment.getExternalStorageState()“/ mnt / sdcard”와 같은 내부 SD 마운트 지점에 대한 경로를 반환합니다. 그러나 문제는 외부 SD에 관한 것입니다. “/ mnt / sdcard / external_sd”와 같은 경로를 얻으려면 어떻게해야합니까 (장치마다 다를 수 있음)?

mount파일 시스템 이름으로 명령 출력을 필터링하는 것으로 끝날 것 입니다. 그러나 나는이 방법이 충분히 강력하다는 것을 확신하지 못한다.



답변

Environment.getExternalStorageState() “/ mnt / sdcard”와 같은 내부 SD 마운트 지점으로 경로를 반환합니다

아니요, Environment.getExternalStorageDirectory()장치 제조업체가 “외부 저장소”로 간주 한 모든 것을 말합니다. 일부 장치에서는 SD 카드와 같은 이동식 미디어입니다. 일부 장치에서는 장치 플래시의 일부입니다. 여기서 “외부 저장 장치”는 “Android 1.x 및 2.x에 대해”호스트 시스템에 장착 된 경우 USB 대용량 저장 모드를 통해 액세스 할 수있는 항목 “을 의미합니다.

그러나 문제는 외부 SD에 관한 것입니다. “/ mnt / sdcard / external_sd”와 같은 경로를 얻는 방법 (장치마다 다를 수 있음)?

안드로이드는 위에서 설명한 것처럼 외부 저장소 외에 “외부 SD”라는 개념이 없습니다.

장치 제조업체에서 외장 스토리지를 온보드 플래시로 선택하고 SD 카드를 사용하기로 선택한 경우 해당 제조업체에 문의하여 SD 카드를 사용할 수 있는지 여부 (보증되지 않음) 및 규칙이 무엇인지 확인해야합니다. 사용할 경로와 같은 사용.


최신 정보

최근 주목할 사항 :

먼저 Android 4.4 이상에서는 getExternalFilesDirs()및에서 반환 할 수있는 미디어의 위치를 ​​제외하고 이동식 미디어 (예 : “외부 SD”)에 대한 쓰기 액세스 권한이 없습니다 getExternalCacheDirs(). 참조 데이브 스미스의 우수한 분석 은 낮은 수준의 세부 정보를 원하는 경우 특히이의를.

둘째, 착탈식 미디어 액세스가 Android SDK의 일부인지 여부에 대해 다른 사람이 얽매이지 않도록 Dianne Hackborn의 평가는 다음과 같습니다.

… 유의 사항 : Android 4.4까지 공식 Android 플랫폼은 두 가지 특별한 경우를 제외하고는 SD 카드를 전혀 지원하지 않았습니다 . 외부 저장소가 SD 카드 인 구식 학교 저장소 레이아웃 (현재 플랫폼에서 여전히 지원됨) Android 3.0에 추가 된 작은 기능으로 SD 카드를 추가로 스캔하여 미디어 제공 업체에 추가하고 앱에 파일에 대한 읽기 전용 액세스 권한을 부여합니다 (현재 플랫폼에서도 여전히 지원됨).

Android 4.4는 애플리케이션이 실제로 SD 카드를 사용하여 저장하도록 허용 한 최초의 플랫폼 릴리스입니다. 그 전에는 지원되지 않는 개인 API를 통해 액세스했습니다. 우리는 이제 플랫폼에서 상당히 풍부한 API를 사용하여 애플리케이션이 SD 카드를 이전보다 더 나은 방식으로 지원되는 방식으로 사용할 수있게되었습니다. 앱에서 권한을 가지며 특별한 권한이 없어도 파일 선택기를 통과하는 한 SD 카드의 다른 파일에 액세스 할 수 있습니다.


답변

여기에있는 몇 가지 답변을 기반으로 다음 솔루션을 생각해 냈습니다.

암호:

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

답변

ListPreference사용자가 무언가를 저장하려는 위치를 선택 해야하는 위치 를 사용하는 응용 프로그램이 있습니다.

이 응용 프로그램에서 나는 스캔 /proc/mounts/system/etc/vold.fstabsdcard에 대한 마운트 지점을. 각 파일의 마운트 지점을 두 개의 별도 ArrayLists에 저장했습니다.

그런 다음 한 목록을 다른 목록과 비교하고 두 목록에 모두없는 버려진 항목을 비교했습니다. 그것은 각 sdcard에 대한 루트 경로 목록을 제공했습니다.

거기에서, 내가 가진 경로를 테스트 File.exists(), File.isDirectory()File.canWrite(). 해당 테스트 중 하나라도 거짓이면 목록에서 해당 경로를 삭제했습니다.

목록에 남은 것이 무엇이든, 나는 값 속성에 String[]의해 사용될 수 있도록 배열 로 변환했습니다 ListPreference.

여기에서 코드를 볼 수 있습니다 : http://sapienmobile.com/?p=204


답변

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

답변

모든을 검색하려면 외부 저장소 ( 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 드라이브를 제외하기 위해 보조 디렉토리 목록을 필터링 할 수 있습니다.

어쨌든 하드 코딩 된 경로를 사용하는 것은 항상 나쁜 접근 방식입니다 (특히 모든 제조업체가 원하는대로 변경할 수있는 경우).


답변

Richard와 마찬가지로 / 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;
    }
}

답변

읽기 /proc/mounts(표준 Linux 파일) 및 vold 데이터 ( /system/etc/vold.conf) 에 대한 교차 검사를 통해 추가 SD 카드가 마운트 된 위치를 찾을 수 있습니다. 그리고 반환 된 위치 Environment.getExternalStorageDirectory()는 vold 구성 (일부 장치에서는 마운트 해제 할 수없는 내부 저장 장치)에 나타나지 않을 수 있지만 여전히 목록에 포함되어야합니다. 그러나 이를 사용자 에게 설명 하는 좋은 방법을 찾지 못했습니다 .