分享

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

 昵称48946089 2019-05-28

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

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

如何获取外置SD卡路径,由于不同厂商定制系统不同,没有统一的方法,可以通过运行时解析安装信息,详细可看下文

在开发中可能会遇到,向SD卡存储文件或让用户选择存储路径,你只有知道了路径才能创建文件,如内置存储卡路径innerPath =“/存储/模拟/ 0”,外置存储卡路径externalPath = “/存储/ extSdCard”。内置存储卡的路径好弄,一行代码就行,而外置内存卡路径就比较难搞定,因为还要兼顾不同手机的适配问题,不同手机路径名字不同,因为不同手机系统可能被定制过,你懂的。

第一种方式(推荐):

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

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

5.0版本以下可以用反射

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

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

第二种方式:

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

  1. <span style="font-size:14px;">public class SDCardUtil {
  2. public final String TAG = "SDCardUtils";
  3. public final static String DIR_SINGLE_SDCARD_NAME = "内置存储卡";
  4. public final static String DIR_SDCARD_NAME = "内置存储卡";
  5. public final static String DIR_EXT_SDCARD_NAME = "扩展存储卡";
  6. // public final static String DIR_UDISK_NAME = "USB存储设备";
  7. private static String SDCARD_PATH = getSDCardPath();
  8. // 系统目录链接,key是linkPath,value是realPath
  9. public static HashMap<String, String> sysLinkToReal = new HashMap<String, String>();
  10. // 系统目录的反向链接, key是realPath,value是linkPath
  11. public static HashMap<String, String> sysRealToLink = new HashMap<String, String>();
  12. public static boolean isMounted() {
  13. return Environment.getExternalStorageState().equals(
  14. Environment.MEDIA_MOUNTED);
  15. }
  16. public static String getSDCardPath() {
  17. return Environment.getExternalStorageDirectory().getAbsolutePath();
  18. }
  19. /**
  20. * 是否能通过基本过滤
  21. *
  22. * @param str
  23. * @return
  24. */
  25. private static boolean testBasicFilter(String str) {
  26. String[] keys = new String[] { "sd", "emmc", "hwuserdata", "udisk",
  27. "ext_card", "usbotg", "disk1", "disk2", "disk3", "disk4",
  28. "usbdrivea", "usbdriveb", "usbdrivec", "usbdrived", "storage",
  29. "external" };
  30. int len = keys.length;
  31. for (int i = 0; i < len; i++) {
  32. if (str.contains(keys[i])) {
  33. return true;
  34. }
  35. }
  36. return false;
  37. }
  38. public static boolean sdCardCanWrite(String path) {
  39. if (path == null) {
  40. return false;
  41. }
  42. File SdCardRoot = new File(path);
  43. if (SdCardRoot.canWrite() == false) {
  44. return false;
  45. }
  46. if (Build.VERSION.SDK_INT >= 19) {
  47. // canWrite() 在4.4系统不起作用,只要路径存在总是返回true
  48. File testPath = new File(new File(path), ".testwrite"
  49. + String.valueOf(System.currentTimeMillis()));
  50. if (testPath.mkdirs()) {
  51. testPath.delete();
  52. return true;
  53. } else {
  54. return false;
  55. }
  56. } else {
  57. return true;
  58. }
  59. }
  60. public static ArrayList<SDCardStat> getSDCardStats(Context context) {
  61. ArrayList<SDCardStat> list = new ArrayList<SDCardStat>();
  62. try {
  63. Process process = Runtime.getRuntime().exec("mount");
  64. BufferedReader br = new BufferedReader(new InputStreamReader(
  65. process.getInputStream()));
  66. // BufferedReader br = new BufferedReader(new InputStreamReader(new
  67. // FileInputStream("/mnt/sdcard/sdcards_info/mount.log")));
  68. String str, lowerStr;
  69. while ((str = br.readLine()) != null) {
  70. lowerStr = str.toLowerCase();
  71. // Log.d("gaolei", "str---------line-----"+str);
  72. if(str.contains("/storage/")){
  73. // MainActivity.filterBuilder.append("\nstorage:\n");
  74. MainActivity.filterBuilder.append("storage----: "+str+"\n\n");
  75. }
  76. // if(str.contains("/sd/")){
  77. // MainActivity.filterBuilder.append("sd:\n\n");
  78. // MainActivity.filterBuilder.append(str+"\n\n");
  79. // }
  80. // Utils.writeLog("getSDCardStats: " + lowerStr);
  81. if (!testBasicFilter(lowerStr)) {
  82. continue;
  83. }
  84. String[] cols = str.split("\\s+");
  85. if (cols == null) {
  86. continue;
  87. }
  88. String path = findSDCardPath(cols);
  89. Log.d("gaolei", "path--------0-------"+path);
  90. if (TextUtils.isEmpty(path)) {
  91. // path = findUDiskPath(cols);
  92. // if (!TextUtils.isEmpty(path)) {
  93. // SDCardStat.Format format = findSDCardFormat(cols);
  94. // if (format != null) {
  95. // UDiskStat = new SDCardStat(path,
  96. // format, 1025);
  97. // }
  98. // }
  99. continue;
  100. }
  101. SDCardStat.Format format = findSDCardFormat(cols);
  102. if (format == null) {
  103. continue;
  104. }
  105. int minorIdx = (Format.vfat == format || Format.exfat == format || Format.texfat == format) ? findVoldDevNodeMinorIndex(cols)
  106. : -100;
  107. SDCardStat stat = new SDCardStat(path, format, minorIdx);
  108. Log.d("gaolei", "path--------1-------"+path);
  109. if (!compareData(list, stat.totalSize)) {
  110. continue;
  111. }
  112. // MainActivity.filterBuilder.append("path----: "+path+"\n\n");
  113. // 4.4以上版本修改trootPath路径,因为4.4及以上版本不支持往外置SD卡根目录写权限
  114. if (android.os.Build.VERSION.SDK_INT >= 19) {
  115. if (!SDCardUtil.sdCardCanWrite(path)) {
  116. stat.canWrite = false;
  117. File[] filePath = ContextCompat.getExternalFilesDirs(
  118. context, null);
  119. if (filePath != null) {
  120. for (File f : filePath) {
  121. if (f != null) {
  122. if (f.getAbsolutePath().startsWith(path)) {
  123. stat.rootPath = f.getAbsolutePath();
  124. Log.d("gaolei", "path--------if-------"+path);
  125. MainActivity.filterBuilder.append("path--if--: "+path+"\n\n");
  126. list.add(stat);
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. } else {
  133. Log.d("gaolei", "path--------else-------"+path);
  134. MainActivity.filterBuilder.append("path--else--: "+path+"\n\n");
  135. list.add(stat);
  136. }
  137. } else {
  138. MainActivity.filterBuilder.append("path--other--: "+path+"\n\n");
  139. Log.d("gaolei", "path--------other-------"+path);
  140. list.add(stat);
  141. }
  142. }
  143. } catch (IOException e) {
  144. e.printStackTrace();
  145. return list;
  146. }
  147. list = sortSDCardList(list);
  148. for (int idx = 0, size = list.size(); idx < size; idx++) {
  149. if (idx == 0) {
  150. list.get(0).name = (size == 1) ? DIR_SINGLE_SDCARD_NAME
  151. : DIR_SDCARD_NAME;
  152. } else if (idx == 1) {
  153. list.get(1).name = DIR_EXT_SDCARD_NAME;
  154. } else {
  155. list.get(idx).name = DIR_EXT_SDCARD_NAME + idx;
  156. }
  157. }
  158. // 将U盘放最后
  159. // if (UDiskStat != null) {
  160. // UDiskStat.name = DIR_UDISK_NAME;
  161. // list.add(UDiskStat);
  162. // }
  163. return list;
  164. }
  165. private static boolean checkSDCardExistByRootPath(String path,
  166. ArrayList<SDCardStat> list) {
  167. boolean existFlag = false;
  168. for (SDCardStat stat : list) {
  169. if (SDCARD_PATH.equals(stat.rootPath)) {
  170. existFlag = true;
  171. break;
  172. }
  173. }
  174. return existFlag;
  175. }
  176. /**
  177. * 根据设备挂载次序排序SDCard
  178. *
  179. * @param list
  180. * @return
  181. */
  182. private static ArrayList<SDCardStat> sortSDCardList(
  183. ArrayList<SDCardStat> list) {
  184. ArrayList<SDCardStat> resultList = new ArrayList<SDCardStat>();
  185. int minIdx = 0;
  186. for (SDCardStat stat : list) {
  187. if (minIdx == 0) {
  188. resultList.add(stat);
  189. minIdx = stat.voldMinorIdx;
  190. continue;
  191. }
  192. if (stat.voldMinorIdx < minIdx
  193. || isInnerSdcard(stat.rootPath, stat.totalSize)) {
  194. resultList.add(0, stat);
  195. minIdx = stat.voldMinorIdx;
  196. } else {
  197. resultList.add(stat);
  198. }
  199. }
  200. return resultList;
  201. }
  202. private final static long SD_PHY_SIZE_1G = 1000 * 1000 * 1000;
  203. private final static long SD_LOGIC_SIZE_1G = 1024 * 1024 * 1024;
  204. private final static double SD_LOGIC_DIFF = SD_LOGIC_SIZE_1G
  205. / (double) SD_PHY_SIZE_1G;
  206. static boolean nCF3(int n) {
  207. boolean boo = true;
  208. String s = Integer.toBinaryString(n);
  209. byte[] b = s.getBytes();
  210. for (int i = 1; i < b.length; i++) {
  211. if (b[i] != 48) {
  212. boo = false;
  213. break;
  214. }
  215. }
  216. return boo;
  217. }
  218. private static boolean isPhySize(long totalSize) {
  219. boolean result = false;
  220. long count = totalSize / SD_PHY_SIZE_1G;
  221. if (count % 2 == 0) {
  222. count = count + 0;
  223. } else {
  224. count = count + 1;
  225. }
  226. if (!nCF3((int) count) || 0 >= totalSize) {
  227. return result;
  228. }
  229. double real_diff = SD_LOGIC_SIZE_1G * count / (double) totalSize;
  230. // 1.063 <= real_diff <= 1.083
  231. result = real_diff >= SD_LOGIC_DIFF - 0.01
  232. && real_diff <= SD_LOGIC_DIFF + 0.01;
  233. return result;
  234. }
  235. private static boolean isInnerSdcard(String path, long totalSize) {
  236. try {
  237. if (!path.endsWith("/")) {
  238. path = path + "/";
  239. }
  240. return !isPhySize(totalSize)
  241. && (Environment.getExternalStorageDirectory()
  242. .getAbsoluteFile().getCanonicalPath() + "/")
  243. .equals(path);
  244. } catch (IOException e) {
  245. return false;
  246. }
  247. }
  248. /**
  249. * 根据mount信息解析sdcard路径
  250. *
  251. * @param mountInfo
  252. * @return
  253. */
  254. private static String findSDCardPath(String[] mountInfo) {
  255. String lowerStr;
  256. for (String col : mountInfo) {
  257. lowerStr = col.toLowerCase();
  258. // Log.d("gaolei", "lowerStr--------------"+lowerStr);
  259. // lenovo 部分手机会把扩展卡bind镜像 /mnt/extrasd_bind
  260. if ((lowerStr.contains("sd") && !lowerStr.contains("extrasd_bind"))
  261. || lowerStr.contains("emmc")
  262. || lowerStr.contains("ext_card")
  263. || lowerStr.contains("external_sd")
  264. || lowerStr.contains("usbstorage")) {
  265. String pDir = getParentPath(col);
  266. // onda平板 扩展卡 /mnt/sdcard/external_sdcard, 三星note扩展卡
  267. // /mnt/sdcard/external_sd
  268. // Sony C6603 扩展卡 /storage/removable/sdcard1
  269. if (pDir.equals(getParentPath(SDCARD_PATH))
  270. || pDir.equals(SDCARD_PATH)
  271. || pDir.equals(SDCARD_PATH + "/")
  272. || pDir.equals("/storage/")
  273. || pDir.equals("/storage/removable/")) {
  274. return col;
  275. }
  276. }
  277. if ((col.contains("/storage/") && !col.contains("self") && !col
  278. .contains("legacy"))) {
  279. Log.d("gaolei", "storage--------------"+col);
  280. return col;
  281. }
  282. if (col.equals("/mnt/ext_sdcard")) {
  283. // 华为p6扩展卡
  284. return col;
  285. }
  286. if (col.equals("/udisk")) {
  287. // coolpad 内置卡 /udisk
  288. return col;
  289. }
  290. if (col.equals("/HWUserData")) {
  291. // 部分 huawei 内置卡 /HWUserData
  292. return col;
  293. }
  294. if (col.equals("/storage/external")) {
  295. // coolpad8720l 外置卡
  296. return col;
  297. }
  298. if (col.equals("/Removable/MicroSD")) {
  299. // ASUS_T00G
  300. return col;
  301. }
  302. }
  303. return null;
  304. }
  305. /**
  306. * 获取SD卡显示路径.<br>
  307. * 类似<strong>/storage/emulated/0</strong>不需要显示路径.<br>
  308. * 类似<strong>/storage/extSdCard/Android/data/{pageageName}/files</strong>
  309. * 只显示从<strong>/Android</strong>开头的路径.
  310. *
  311. * @param path
  312. * @return
  313. */
  314. public static String getShowSDPath(SDCardStat stat) {
  315. String showPath = "";
  316. String path = stat.rootPath;
  317. if (android.os.Build.VERSION.SDK_INT >= 19 && !stat.canWrite) {
  318. int index = path.indexOf("Android/data/");
  319. if (index != -1) {
  320. showPath = path.substring(index);
  321. }
  322. } else {
  323. showPath = path.substring(path.lastIndexOf(File.separator) + 1);
  324. if (showPath.equals("0")) {
  325. showPath = "";
  326. }
  327. }
  328. return showPath;
  329. }
  330. /**
  331. * 根据mount信息解析sdcard分区格式
  332. *
  333. * @param mountInfo
  334. * @return
  335. */
  336. private static SDCardStat.Format findSDCardFormat(String[] mountInfo) {
  337. int formatMinLength = 0;
  338. int formatMaxLength = 0;
  339. for (SDCardStat.Format format : SDCardStat.Format.values()) {
  340. int len = format.toString().length();
  341. if (len > formatMaxLength) {
  342. formatMaxLength = len;
  343. } else if (len < formatMinLength) {
  344. formatMinLength = len;
  345. }
  346. }
  347. for (String col : mountInfo) {
  348. if (col.length() < formatMinLength
  349. || col.length() > formatMaxLength) {
  350. continue;
  351. }
  352. for (SDCardStat.Format format : SDCardStat.Format.values()) {
  353. if (format.toString().equals(col)) {
  354. return format;
  355. }
  356. }
  357. }
  358. return null;
  359. }
  360. /**
  361. * 解析Vold设备号
  362. *
  363. * @param mountInfo
  364. * @return
  365. */
  366. private static String findVoldDevNodeIndex(String[] mountInfo) {
  367. if (mountInfo == null || mountInfo.length <= 0) {
  368. return null;
  369. }
  370. String voldInfo = mountInfo[0];
  371. if (TextUtils.isEmpty(voldInfo)) {
  372. return null;
  373. }
  374. return voldInfo.replaceFirst("/dev/block/vold/", "");
  375. }
  376. /**
  377. * 解析Vold(vfat格式)次设备号
  378. *
  379. * @param mountInfo
  380. * @return
  381. */
  382. private static int findVoldDevNodeMinorIndex(String[] mountInfo) {
  383. String voldInfo = findVoldDevNodeIndex(mountInfo);
  384. if (TextUtils.isEmpty(voldInfo)) {
  385. return -1;
  386. }
  387. String[] infos = voldInfo.split(":");
  388. if (infos == null || infos.length < 2) {
  389. return -1;
  390. }
  391. return Integer.valueOf(infos[1]);
  392. }
  393. /**
  394. * 计算目标路径的磁盘使用情况
  395. *
  396. * @param path
  397. * @return
  398. */
  399. private static DiskStat getDiskCapacity(String path) {
  400. File file = new File(path);
  401. if (!file.exists()) {
  402. return null;
  403. }
  404. StatFs stat = new StatFs(path);
  405. long blockSize = stat.getBlockSize();
  406. long totalBlockCount = stat.getBlockCount();
  407. long feeBlockCount = stat.getAvailableBlocks();
  408. return new DiskStat(blockSize * feeBlockCount, blockSize
  409. * totalBlockCount);
  410. }
  411. /**
  412. * 取上一级路径
  413. *
  414. * @param path
  415. * @return
  416. */
  417. public static String getParentPath(String path) {
  418. if (path != null && path.length() > 0) {
  419. path = path.substring(0, path.length() - 1); // 去掉最后一个字符 , 以兼容以“/”
  420. // 结尾的路径
  421. return path.substring(0, path.lastIndexOf(File.separator) + 1);
  422. } else {
  423. return "";
  424. }
  425. }
  426. // 1.判断如果总容量小于2G,则排除 2.排除内置或外置重复路径
  427. public static boolean compareData(ArrayList<SDCardStat> list,
  428. long capacity) {
  429. //排除内置或外置重复路径
  430. if (list.size() > 0) {
  431. for (int i = 0; i < list.size(); i++) {
  432. if (list.get(i).totalSize==capacity) {
  433. Log.d("gaolei", "duplicate-------------------------");
  434. return false;
  435. }
  436. }
  437. }
  438. //判断如果总容量小于2G
  439. if (capacity/ 1073741824<2) {
  440. Log.d("gaolei", "capacity/ 1073741824-------------------------"+capacity/ 1073741824);
  441. return false;
  442. }
  443. return true;
  444. }
  445. public static class SDCardStat {
  446. public String rootPath;
  447. public String excludePath; // 排除路径, 某些手机会将扩展卡挂载在sdcard下面
  448. public String name;
  449. public Format format;
  450. // public long usedSize;
  451. public long totalSize;
  452. public long freeSize;
  453. public boolean isCaseSensitive;
  454. public int voldMinorIdx;
  455. // voldMinorIdx 小于此值的为sdcard内置设备,大于此值的是u盘
  456. public static int SDCARD_MAX_COUNT = 1024;
  457. /**
  458. * Android4.4增加了SD卡读写权限设置,分为内置存储和外置SD卡,对权限见下表:<br>
  459. * <table width="60%" border="1" align="center">
  460. * <tr>
  461. * <th align="center">Action</th>
  462. * <th align="center">Primary</th>
  463. * <th align="center">Secondary</th>
  464. * </tr>
  465. * <tbody>
  466. * <tr>
  467. * <td>Read Top-Level Directories</td>
  468. * <td align="center">R</td>
  469. * <td align="center">R</td>
  470. * </tr>
  471. * <tr>
  472. * <td>Write Top-Level Directories</td>
  473. * <td align="center">W</td>
  474. * <td align="center">N</td>
  475. * </tr>
  476. * <tr>
  477. * <td>Read My Package’s Android Data Directory</td>
  478. * <td align="center">Y</td>
  479. * <td align="center">Y</td>
  480. * </tr>
  481. * <tr>
  482. * <td>Write My Package’s Android Data Directory</td>
  483. * <td align="center">Y</td>
  484. * <td align="center">Y</td>
  485. * </tr>
  486. * <tr>
  487. * <td>Read Another Package’s Android Data Directory</td>
  488. * <td align="center">R</td>
  489. * <td align="center">R</td>
  490. * </tr>
  491. * <tr>
  492. * <td>Write Another Package’s Android Data Directory</td>
  493. * <td align="center">W</td>
  494. * <td align="center">N</td>
  495. * </tr>
  496. * </tbody>
  497. * </table>
  498. * <p style="text-align: center;">
  499. * <strong>R = With Read Permission, W = With Write Permission, Y =
  500. * Always, N = Never </strong>
  501. * </p>
  502. * 根据上面表格判断SD类型,这个属性代表了Write Top-Level Directories的Secondary(外置SD卡).<br>
  503. * 由于部分手机厂商没有遵循Google新的SD卡规范,所以在部分Android4.4手机上外置SD卡的根目录仍然有读写
  504. * 权限.所以只有在Android4.4以上手机,并且外置SD卡不可写的情况此属性才为<strong>false</strong>.
  505. */
  506. public boolean canWrite = true;
  507. public SDCardStat(String path, Format format, int voldMinorIdx,
  508. String excludePath) {
  509. DiskStat stat = getDiskCapacity(path);
  510. if (stat != null) {
  511. this.freeSize = stat.free;
  512. this.totalSize = stat.total;
  513. }
  514. this.rootPath = path;
  515. this.format = format;
  516. this.isCaseSensitive = checkCaseSensitive(format);
  517. // this.name = getSDCardName(path);
  518. this.voldMinorIdx = voldMinorIdx;
  519. this.excludePath = excludePath;
  520. }
  521. public SDCardStat(String path, Format format, int voldMinorIdx) {
  522. this(path, format, voldMinorIdx, "");
  523. }
  524. public static enum Format {
  525. vfat, exfat, ext4, fuse, sdcardfs, texfat
  526. }
  527. public boolean checkCaseSensitive(Format format) {
  528. return (format == Format.vfat || format == Format.exfat) ? false
  529. : true;
  530. }
  531. public void setExcludePath(String excludePath) {
  532. DiskStat excludeStat = getDiskCapacity(excludePath);
  533. if (excludeStat != null) {
  534. this.freeSize -= excludeStat.free;
  535. this.totalSize -= excludeStat.total;
  536. }
  537. this.excludePath = excludePath;
  538. }
  539. public void refreshDiskCapacity() {
  540. DiskStat stat = getDiskCapacity(this.rootPath);
  541. if (stat != null) {
  542. this.freeSize = stat.free;
  543. this.totalSize = stat.total;
  544. }
  545. }
  546. }
  547. public static class DiskStat {
  548. public long free;
  549. public long total;
  550. public DiskStat(long free, long total) {
  551. this.free = free;
  552. this.total = total;
  553. }
  554. }
  555. }</span>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多