2024-04-06

【Kotlin】查詢 Android 可用的 app 專屬外部儲存空間

參考資料 ----


Android app,可儲存 app 自身專屬(用) 檔案於 內部儲存空間外部儲存空間,內部儲存空間很小,只適合存放小容量的檔案,若不是有特殊需求,官方建議存於外部儲存空間。

雖然是使用外部儲存空間,但因為僅供 app 自身專用,所以並不須向系統請求權限。

  1.  
  2. class MainActivity : AppCompatActivity() {
  3. private val TAG = "MainActivity"
  4.  
  5. override fun onCreate(savedInstanceState: Bundle?) {
  6. super.onCreate(savedInstanceState)
  7. setContentView(R.layout.activity_main)
  8. // 查詢磁碟區狀態
  9. val sExternalStorage = Environment.getExternalStorageState()
  10. Log.d(TAG, "可用外部空間媒體資訊: $sExternalStorage") // mounted: 可讀/寫
  11. // 狀態有: MEDIA_UNKNOWN, MEDIA_REMOVED, MEDIA_UNMOUNTED, MEDIA_CHECKING, MEDIA_NOFS, MEDIA_MOUNTED, MEDIA_MOUNTED_READ_ONLY, MEDIA_SHARED, MEDIA_BAD_REMOVAL, MEDIA_UNMOUNTABLE
  12. // 以前的 Android 因為 ROM 小, 多半會有 SD 卡插槽
  13. // 現在 ROM 變大了, 也漸漸不提供 SD 卡插槽了, 並模擬出一塊虛擬 ROM
  14. val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(applicationContext, null)
  15. Log.d(TAG, "可用外部空間媒體: ${externalStorageVolumes.size} 個") // 2 個, 1 個實體 SD 卡, 1 個虛擬 ROM
  16. for(xx in externalStorageVolumes.indices) {
  17. val primaryExternalStorage = externalStorageVolumes[xx]
  18. Log.d(TAG, "可用外部空間 $xx: $primaryExternalStorage")
  19. // 可用外部空間 0: /storage/emulated/0/Android/data/[您的 app 完整 package 名]/files, 虛擬 ROM 通常會是第 1 個
  20. // 可用外部空間 1: /storage/9E1E-5F1E/Android/data/[您的 app 完整 package 名]/files, 實體 SD 卡
  21. }
  22. if(Environment.getExternalStorageState()==Environment.MEDIA_MOUNTED) {
  23. // 有外部儲存空間媒體, 且可 讀/寫
  24. // 官方強烈建議, 若沒有特殊需要, 優先使用第 1 個可用外部空間
  25. val mFilePath = externalStorageVolumes[0]
  26. Log.d(TAG, "mFilePath: $mFilePath") // /storage/emulated/0/Android/data/[您的 app 完整 package 名]/files
  27. val mExtdir = File(getExternalFilesDir(null), "Chkspace")
  28.  
  29. // 查詢可用空間
  30. val mScope = CoroutineScope(Job() + Dispatchers.IO)
  31. mScope.launch {
  32. var mBytes: Long = 0
  33. // 1. 由於在 Android8(Oreo) 推出新的 API26,
  34. // storageManager.getUuidForPath 不能在主執行緒呼叫, 因此要在 coroutine 內執行
  35. // 2. 舊的 API 在未來會被棄用, 所以要分開處理
  36. if(Build.VERSION.SDK_INT>=26) {
  37. val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager
  38. val uuid = storageManager.getUuidForPath(externalStorageVolumes[0])
  39. try {
  40. mBytes = storageManager.getAllocatableBytes(uuid)
  41. } catch(err: IOException) {
  42. err.printStackTrace()
  43. }
  44. } else {
  45. val path = Environment.getDataDirectory()
  46. val stat = StatFs(path.path)
  47. val blockSize: Long = stat.blockSizeLong
  48. val availableBlocks: Long = stat.availableBlocksLong
  49. mBytes = availableBlocks * blockSize
  50. }
  51. val formatter = DecimalFormat("#,###")
  52. Log.d(TAG, "可用剩餘外部儲存空間 = ${formatter.format(mBytes)} ")
  53.  
  54. if(mBytes>=1024) {
  55. val mKB = (mBytes/1024) // 換算為 kb
  56. if(mKB>=1024) {
  57. val mmMB = (mKB/1024) // 換算為 mb
  58. if(mmMB>=1024) {
  59. val mGB = (mmMB/1024) // 換算為 gb
  60. Log.d(TAG, "可用剩餘外部儲存空間 = $mGB gb")
  61. } else
  62. Log.d(TAG, "可用剩餘外部儲存空間 = $mmMB mb")
  63. } else {
  64. Log.d(TAG, "可用剩餘外部儲存空間 = $mKB kb")
  65. }
  66. } else {
  67. Log.d(TAG, "可用剩餘外部儲存空間 = $mBytes bytes")
  68. }
  69. }
  70. }
  71. }
  72. }
  73.  


沒有留言:

張貼留言