Android 获取内置和外置存储卡的路径及总共、可用空间

在实现这个功能之前可以弄清几个获取系统路径的方法:

  1. Context.getCacheDir().getPath() ;  对应路径:/ data /user / 0 / <应用程序包>/ cache(路径不可见
  2. Context.getFilesDir().getPath() ;  对应路径:/ data / user / 0 / <应用程序包> /files(路径不可见
  3. Context.getExternalCacheDir().getPath();  对应路径:Android/data/<应用程序包>/cache  (如果想缓存文件可用这个,内存不足时删除,节约用户空间
  4. Context.getExternalFilesDir("glide").getPath();  对应路径:Android/data/<应用程序包>/files/glide  (如果想缓存文件可用这个,会随应用删除而删除,节约用户空间
  5. context.getDir("config", Context.MODE_PRIVATE);  对应路径:Android/data/包名/app_config (其中包名后面的app_是为调用时,系统自己加上的,Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问
  6. Context.getPackageName()  : <应用程序包>
  7. Context.getPackageResourcePath()  : /data/app/com.zhd-1.apk
  8. Context.getPackageResourcePath() :用于获取该程序的安装包路径
  9. Context.getDatabasePath():用于获取/ data / <应用程序包> / databases 目录
  10. Environment.getRootDirectory().getPath()    : /system
  11. Environment.getDataDirectory().getPath(): /data
  12. Environment.getExternalStorageDirectory().getPath() :用于获得内部存储卡路径

Android 获取内置和外置存储卡的路径及总共、可用空间_第1张图片

 

第一种方式(推荐):

5.0版本及以上可用ContextCompat.getExternalFilesDirs() 获取到内置和外置存储卡路径

if (android.os.Build.VERSION.SDK_INT >= 19) {
    File[] files = ContextCompat.getExternalFilesDirs(
            BaseApplication.mContext, null)
}

5.0版本以下可以用反射

 pathsTmp = (String[]) methodGetPaths.invoke(sManager);

详细代码代码地址:https://github.com/gaoleicoding/StorageCard

第二种方式:

根据安装信息解析SD卡路径。测试通很多型号手机项目中以运用,主要代码如下:

public class SDCardUtil {

    public final String TAG = "SDCardUtils";

    public final static String DIR_SINGLE_SDCARD_NAME = "内置存储卡";
    public final static String DIR_SDCARD_NAME = "内置存储卡";
    public final static String DIR_EXT_SDCARD_NAME = "扩展存储卡";
    // public final static String DIR_UDISK_NAME = "USB存储设备";

    private static String SDCARD_PATH = getSDCardPath();

    // 系统目录链接,key是linkPath,value是realPath
    public static HashMap sysLinkToReal = new HashMap();
    // 系统目录的反向链接, key是realPath,value是linkPath
    public static HashMap sysRealToLink = new HashMap();

    public static boolean isMounted() {
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

    public static String getSDCardPath() {
        return Environment.getExternalStorageDirectory().getAbsolutePath();
    }

    /**
     * 是否能通过基本过滤
     * 
     * @param str
     * @return
     */
    private static boolean testBasicFilter(String str) {
        String[] keys = new String[] { "sd", "emmc", "hwuserdata", "udisk",
                "ext_card", "usbotg", "disk1", "disk2", "disk3", "disk4",
                "usbdrivea", "usbdriveb", "usbdrivec", "usbdrived", "storage",
                "external" };
        int len = keys.length;
        for (int i = 0; i < len; i++) {
            if (str.contains(keys[i])) {
                return true;
            }
        }
        return false;
    }

    public static boolean sdCardCanWrite(String path) {
        if (path == null) {
            return false;
        }

        File SdCardRoot = new File(path);
        if (SdCardRoot.canWrite() == false) {
            return false;
        }

        if (Build.VERSION.SDK_INT >= 19) {
            // canWrite() 在4.4系统不起作用,只要路径存在总是返回true
            File testPath = new File(new File(path), ".testwrite"
                    + String.valueOf(System.currentTimeMillis()));

            if (testPath.mkdirs()) {
                testPath.delete();
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    public static ArrayList getSDCardStats(Context context) {
        ArrayList list = new ArrayList();

        try {
            Process process = Runtime.getRuntime().exec("mount");
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    process.getInputStream()));
            // BufferedReader br = new BufferedReader(new InputStreamReader(new
            // FileInputStream("/mnt/sdcard/sdcards_info/mount.log")));
            String str, lowerStr;
            while ((str = br.readLine()) != null) {
                lowerStr = str.toLowerCase();
//                Log.d("gaolei", "str---------line-----"+str);
                if(str.contains("/storage/")){
//                	 MainActivity.filterBuilder.append("\nstorage:\n");
                    MainActivity.filterBuilder.append("storage----:  "+str+"\n\n");
                }
//                if(str.contains("/sd/")){
//                	MainActivity.filterBuilder.append("sd:\n\n");
//                	MainActivity.filterBuilder.append(str+"\n\n");
//                }
                // Utils.writeLog("getSDCardStats: " + lowerStr);
                if (!testBasicFilter(lowerStr)) {
                    continue;
                }
                String[] cols = str.split("\\s+");
                if (cols == null) {
                    continue;
                }

                String path = findSDCardPath(cols);
                Log.d("gaolei", "path--------0-------"+path);
                if (TextUtils.isEmpty(path)) {
                    // path = findUDiskPath(cols);
                    // if (!TextUtils.isEmpty(path)) {
                    // SDCardStat.Format format = findSDCardFormat(cols);
                    // if (format != null) {
                    // UDiskStat = new SDCardStat(path,
                    // format, 1025);
                    // }
                    // }
                    continue;
                }
                SDCardStat.Format format = findSDCardFormat(cols);
                if (format == null) {
                    continue;
                }
                
                int minorIdx = (Format.vfat == format || Format.exfat == format || Format.texfat == format) ? findVoldDevNodeMinorIndex(cols)
                        : -100;
                SDCardStat stat = new SDCardStat(path, format, minorIdx);
                Log.d("gaolei", "path--------1-------"+path);
				if (!compareData(list, stat.totalSize)) {
					continue;
				}
                
//                MainActivity.filterBuilder.append("path----:  "+path+"\n\n");
                // 4.4以上版本修改trootPath路径,因为4.4及以上版本不支持往外置SD卡根目录写权限
                if (android.os.Build.VERSION.SDK_INT >= 19) {
                    if (!SDCardUtil.sdCardCanWrite(path)) {
                        stat.canWrite = false;
                        File[] filePath = ContextCompat.getExternalFilesDirs(
                                context, null);
                        if (filePath != null) {
                            for (File f : filePath) {
                                if (f != null) {
                                    if (f.getAbsolutePath().startsWith(path)) {
                                        stat.rootPath = f.getAbsolutePath();
                                        Log.d("gaolei", "path--------if-------"+path);
                                        MainActivity.filterBuilder.append("path--if--:  "+path+"\n\n");
                                        list.add(stat);
                                        break;
                                    }
                                }
                            }
                        }
                    } else {
                    	Log.d("gaolei", "path--------else-------"+path);
                    	 MainActivity.filterBuilder.append("path--else--:  "+path+"\n\n");
                        list.add(stat);
                    }
                } else {
                	MainActivity.filterBuilder.append("path--other--:  "+path+"\n\n");
                	Log.d("gaolei", "path--------other-------"+path);
                    list.add(stat);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return list;
        }

        list = sortSDCardList(list);

        for (int idx = 0, size = list.size(); idx < size; idx++) {
            if (idx == 0) {
                list.get(0).name = (size == 1) ? DIR_SINGLE_SDCARD_NAME
                        : DIR_SDCARD_NAME;
            } else if (idx == 1) {
                list.get(1).name = DIR_EXT_SDCARD_NAME;
            } else {
                list.get(idx).name = DIR_EXT_SDCARD_NAME + idx;
            }
        }

        // 将U盘放最后
        // if (UDiskStat != null) {
        // UDiskStat.name = DIR_UDISK_NAME;
        // list.add(UDiskStat);
        // }

        return list;
    }

    private static boolean checkSDCardExistByRootPath(String path,
            ArrayList list) {
        boolean existFlag = false;
        for (SDCardStat stat : list) {
            if (SDCARD_PATH.equals(stat.rootPath)) {
                existFlag = true;
                break;
            }
        }
        return existFlag;
    }

    /**
     * 根据设备挂载次序排序SDCard
     * 
     * @param list
     * @return
     */
    private static ArrayList sortSDCardList(
            ArrayList list) {
        ArrayList resultList = new ArrayList();
        int minIdx = 0;
        for (SDCardStat stat : list) {
            if (minIdx == 0) {
                resultList.add(stat);
                minIdx = stat.voldMinorIdx;
                continue;
            }

            if (stat.voldMinorIdx < minIdx
                    || isInnerSdcard(stat.rootPath, stat.totalSize)) {
                resultList.add(0, stat);
                minIdx = stat.voldMinorIdx;
            } else {
                resultList.add(stat);
            }
        }
        return resultList;
    }

    private final static long SD_PHY_SIZE_1G = 1000 * 1000 * 1000;
    private final static long SD_LOGIC_SIZE_1G = 1024 * 1024 * 1024;
    private final static double SD_LOGIC_DIFF = SD_LOGIC_SIZE_1G
            / (double) SD_PHY_SIZE_1G;

    static boolean nCF3(int n) {
        boolean boo = true;

        String s = Integer.toBinaryString(n);

        byte[] b = s.getBytes();

        for (int i = 1; i < b.length; i++) {
            if (b[i] != 48) {
                boo = false;
                break;
            }
        }

        return boo;
    }

    private static boolean isPhySize(long totalSize) {
        boolean result = false;
        long count = totalSize / SD_PHY_SIZE_1G;
        if (count % 2 == 0) {
            count = count + 0;
        } else {
            count = count + 1;
        }
        if (!nCF3((int) count) || 0 >= totalSize) {
            return result;
        }
        double real_diff = SD_LOGIC_SIZE_1G * count / (double) totalSize;
        // 1.063 <= real_diff <= 1.083
        result = real_diff >= SD_LOGIC_DIFF - 0.01
                && real_diff <= SD_LOGIC_DIFF + 0.01;
        return result;
    }

    private static boolean isInnerSdcard(String path, long totalSize) {
        try {
            if (!path.endsWith("/")) {
                path = path + "/";
            }
            return !isPhySize(totalSize)
                    && (Environment.getExternalStorageDirectory()
                            .getAbsoluteFile().getCanonicalPath() + "/")
                            .equals(path);
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 根据mount信息解析sdcard路径
     * 
     * @param mountInfo
     * @return
     */
    private static String findSDCardPath(String[] mountInfo) {
        String lowerStr;
        for (String col : mountInfo) {
            lowerStr = col.toLowerCase();
//            Log.d("gaolei", "lowerStr--------------"+lowerStr);
            // lenovo 部分手机会把扩展卡bind镜像 /mnt/extrasd_bind
           
            if ((lowerStr.contains("sd") && !lowerStr.contains("extrasd_bind"))
                    || lowerStr.contains("emmc")
                    || lowerStr.contains("ext_card")
                    || lowerStr.contains("external_sd")
                    || lowerStr.contains("usbstorage")) {
                String pDir = getParentPath(col);
                // onda平板 扩展卡 /mnt/sdcard/external_sdcard, 三星note扩展卡
                // /mnt/sdcard/external_sd
                // Sony C6603 扩展卡 /storage/removable/sdcard1
                if (pDir.equals(getParentPath(SDCARD_PATH))
                        || pDir.equals(SDCARD_PATH)
                        || pDir.equals(SDCARD_PATH + "/")
                        || pDir.equals("/storage/")
                        || pDir.equals("/storage/removable/")) {
                    return col;
                }
            }
            
            if ((col.contains("/storage/") && !col.contains("self") && !col
					.contains("legacy"))) {
            	Log.d("gaolei", "storage--------------"+col);
				return col;
			}
            if (col.equals("/mnt/ext_sdcard")) {
                // 华为p6扩展卡
                return col;
            }
            if (col.equals("/udisk")) {
                // coolpad 内置卡 /udisk
                return col;
            }
            if (col.equals("/HWUserData")) {
                // 部分 huawei 内置卡 /HWUserData
                return col;
            }
            if (col.equals("/storage/external")) {
                // coolpad8720l 外置卡
                return col;
            }
            if (col.equals("/Removable/MicroSD")) {
                // ASUS_T00G
                return col;
            }
        }
        return null;
    }

    /**
     * 获取SD卡显示路径.
* 类似/storage/emulated/0不需要显示路径.
* 类似/storage/extSdCard/Android/data/{pageageName}/files * 只显示从/Android开头的路径. * * @param path * @return */ public static String getShowSDPath(SDCardStat stat) { String showPath = ""; String path = stat.rootPath; if (android.os.Build.VERSION.SDK_INT >= 19 && !stat.canWrite) { int index = path.indexOf("Android/data/"); if (index != -1) { showPath = path.substring(index); } } else { showPath = path.substring(path.lastIndexOf(File.separator) + 1); if (showPath.equals("0")) { showPath = ""; } } return showPath; } /** * 根据mount信息解析sdcard分区格式 * * @param mountInfo * @return */ private static SDCardStat.Format findSDCardFormat(String[] mountInfo) { int formatMinLength = 0; int formatMaxLength = 0; for (SDCardStat.Format format : SDCardStat.Format.values()) { int len = format.toString().length(); if (len > formatMaxLength) { formatMaxLength = len; } else if (len < formatMinLength) { formatMinLength = len; } } for (String col : mountInfo) { if (col.length() < formatMinLength || col.length() > formatMaxLength) { continue; } for (SDCardStat.Format format : SDCardStat.Format.values()) { if (format.toString().equals(col)) { return format; } } } return null; } /** * 解析Vold设备号 * * @param mountInfo * @return */ private static String findVoldDevNodeIndex(String[] mountInfo) { if (mountInfo == null || mountInfo.length <= 0) { return null; } String voldInfo = mountInfo[0]; if (TextUtils.isEmpty(voldInfo)) { return null; } return voldInfo.replaceFirst("/dev/block/vold/", ""); } /** * 解析Vold(vfat格式)次设备号 * * @param mountInfo * @return */ private static int findVoldDevNodeMinorIndex(String[] mountInfo) { String voldInfo = findVoldDevNodeIndex(mountInfo); if (TextUtils.isEmpty(voldInfo)) { return -1; } String[] infos = voldInfo.split(":"); if (infos == null || infos.length < 2) { return -1; } return Integer.valueOf(infos[1]); } /** * 计算目标路径的磁盘使用情况 * * @param path * @return */ private static DiskStat getDiskCapacity(String path) { File file = new File(path); if (!file.exists()) { return null; } StatFs stat = new StatFs(path); long blockSize = stat.getBlockSize(); long totalBlockCount = stat.getBlockCount(); long feeBlockCount = stat.getAvailableBlocks(); return new DiskStat(blockSize * feeBlockCount, blockSize * totalBlockCount); } /** * 取上一级路径 * * @param path * @return */ public static String getParentPath(String path) { if (path != null && path.length() > 0) { path = path.substring(0, path.length() - 1); // 去掉最后一个字符 , 以兼容以“/” // 结尾的路径 return path.substring(0, path.lastIndexOf(File.separator) + 1); } else { return ""; } } // 1.判断如果总容量小于2G,则排除 2.排除内置或外置重复路径 public static boolean compareData(ArrayList list, long capacity) { //排除内置或外置重复路径 if (list.size() > 0) { for (int i = 0; i < list.size(); i++) { if (list.get(i).totalSize==capacity) { Log.d("gaolei", "duplicate-------------------------"); return false; } } } //判断如果总容量小于2G if (capacity/ 1073741824<2) { Log.d("gaolei", "capacity/ 1073741824-------------------------"+capacity/ 1073741824); return false; } return true; } public static class SDCardStat { public String rootPath; public String excludePath; // 排除路径, 某些手机会将扩展卡挂载在sdcard下面 public String name; public Format format; // public long usedSize; public long totalSize; public long freeSize; public boolean isCaseSensitive; public int voldMinorIdx; // voldMinorIdx 小于此值的为sdcard内置设备,大于此值的是u盘 public static int SDCARD_MAX_COUNT = 1024; /** * Android4.4增加了SD卡读写权限设置,分为内置存储和外置SD卡,对权限见下表:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ActionPrimarySecondary
Read Top-Level DirectoriesRR
Write Top-Level DirectoriesWN
Read My Package’s Android Data DirectoryYY
Write My Package’s Android Data DirectoryYY
Read Another Package’s Android Data DirectoryRR
Write Another Package’s Android Data DirectoryWN
*

* R = With Read Permission, W = With Write Permission, Y = * Always, N = Never *

* 根据上面表格判断SD类型,这个属性代表了Write Top-Level Directories的Secondary(外置SD卡).
* 由于部分手机厂商没有遵循Google新的SD卡规范,所以在部分Android4.4手机上外置SD卡的根目录仍然有读写 * 权限.所以只有在Android4.4以上手机,并且外置SD卡不可写的情况此属性才为false. */ public boolean canWrite = true; public SDCardStat(String path, Format format, int voldMinorIdx, String excludePath) { DiskStat stat = getDiskCapacity(path); if (stat != null) { this.freeSize = stat.free; this.totalSize = stat.total; } this.rootPath = path; this.format = format; this.isCaseSensitive = checkCaseSensitive(format); // this.name = getSDCardName(path); this.voldMinorIdx = voldMinorIdx; this.excludePath = excludePath; } public SDCardStat(String path, Format format, int voldMinorIdx) { this(path, format, voldMinorIdx, ""); } public static enum Format { vfat, exfat, ext4, fuse, sdcardfs, texfat } public boolean checkCaseSensitive(Format format) { return (format == Format.vfat || format == Format.exfat) ? false : true; } public void setExcludePath(String excludePath) { DiskStat excludeStat = getDiskCapacity(excludePath); if (excludeStat != null) { this.freeSize -= excludeStat.free; this.totalSize -= excludeStat.total; } this.excludePath = excludePath; } public void refreshDiskCapacity() { DiskStat stat = getDiskCapacity(this.rootPath); if (stat != null) { this.freeSize = stat.free; this.totalSize = stat.total; } } } public static class DiskStat { public long free; public long total; public DiskStat(long free, long total) { this.free = free; this.total = total; } } }

Android 获取内置和外置存储卡的路径及总共、可用空间_第2张图片
           

 

    代码地址,点击下载......

 

你可能感兴趣的:(原创,Android,内置外置内存卡,路径,总共可用空间)