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() :用于获得内部存储卡路径

 

第一种方式(推荐):

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卡路径。测试通很多型号手机项目中以运用,主要代码如下:

<span style="font-size:14px;">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<String, String> sysLinkToReal = new HashMap<String, String>();
    // 系统目录的反向链接, key是realPath,value是linkPath
    public static HashMap<String, String> sysRealToLink = new HashMap<String, String>();

    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<SDCardStat> getSDCardStats(Context context) {
        ArrayList<SDCardStat> list = new ArrayList<SDCardStat>();

        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<SDCardStat> 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<SDCardStat> sortSDCardList(
            ArrayList<SDCardStat> list) {
        ArrayList<SDCardStat> resultList = new ArrayList<SDCardStat>();
        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卡显示路径.<br>
     * 类似<strong>/storage/emulated/0</strong>不需要显示路径.<br>
     * 类似<strong>/storage/extSdCard/Android/data/{pageageName}/files</strong>
     * 只显示从<strong>/Android</strong>开头的路径.
     * 
     * @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<SDCardStat> 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卡,对权限见下表:<br>
         * <table width="60%" border="1" align="center">
         * <tr>
         * <th align="center">Action</th>
         * <th align="center">Primary</th>
         * <th align="center">Secondary</th>
         * </tr>
         * <tbody>
         * <tr>
         * <td>Read Top-Level Directories</td>
         * <td align="center">R</td>
         * <td align="center">R</td>
         * </tr>
         * <tr>
         * <td>Write Top-Level Directories</td>
         * <td align="center">W</td>
         * <td align="center">N</td>
         * </tr>
         * <tr>
         * <td>Read My Package’s Android Data Directory</td>
         * <td align="center">Y</td>
         * <td align="center">Y</td>
         * </tr>
         * <tr>
         * <td>Write My Package’s Android Data Directory</td>
         * <td align="center">Y</td>
         * <td align="center">Y</td>
         * </tr>
         * <tr>
         * <td>Read Another Package’s Android Data Directory</td>
         * <td align="center">R</td>
         * <td align="center">R</td>
         * </tr>
         * <tr>
         * <td>Write Another Package’s Android Data Directory</td>
         * <td align="center">W</td>
         * <td align="center">N</td>
         * </tr>
         * </tbody>
         * </table>
         * <p style="text-align: center;">
         * <strong>R = With Read Permission, W = With Write Permission, Y =
         * Always, N = Never </strong>
         * </p>
         * 根据上面表格判断SD类型,这个属性代表了Write Top-Level Directories的Secondary(外置SD卡).<br>
         * 由于部分手机厂商没有遵循Google新的SD卡规范,所以在部分Android4.4手机上外置SD卡的根目录仍然有读写
         * 权限.所以只有在Android4.4以上手机,并且外置SD卡不可写的情况此属性才为<strong>false</strong>.
         */
        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;
        }
    }

}</span>


           

 

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值