0
0

Androidデバイスのストレージ容量を取得する

Last updated at Posted at 2024-09-12

アプリでストレージの全体容量を取得したい

ひょんなことで、内部ストレージの全体容量と空き容量を取得する必要性が出てきました
先行調査から「android.os.StatFsを使えばいけるんじゃね?」とのこと

確かに行けそう…
まぁどうやるかもよくわからんし、他サイトも調べてみると

まさにな記事も発見!
さらに調べてみると上の記事でやっている

      val statFs = StatFs(path)
      // 総容量
      val totalSpase: Long = statFs.blockCountLong * statFs.blockSizeLong / 1024L / 1024L
      // 空き容量
      val freeSpase: Long = statFs.availableBlocksLong * statFs.blockSizeLong / 1024L / 1024L

というコードは

      val statFs = StatFs(path)
      // 総容量
      val totalSpase: Long = statFs.getTotalBytes / 1024L / 1024L
      // 空き容量
      val freeSpase: Long = statFs.getFreeBytes / 1024L / 1024L

とやっていることは同じであるようだった(参考サイトどっかいってしまった😭)

さて、そうなればやることはすごく簡単じゃないか!
ということでコードをポチポチ…

class MainActivity : AppCompatActivity() {

    //内部ストレージの全体容量
    fun getAllStorage(context: Context): Long {
        val statFs = StatFs(context.filesDir.absolutePath)
        val totalBytes = statFs.totalBytes
        return totalBytes
    }

    //内部ストレージの空き容量
    fun getFreeStorage(context: Context): Long {
        val statFs = StatFs(context.filesDir.absolutePath)
        val availableBytes = statFs.availableBytes
        return availableBytes
    }

    // バイト単位のサイズを読みやすい形式に変換するヘルパー関数
    private fun formatSize(size: Long): String {
        if (size <= 0) return "0 B"
        
        val units = arrayOf("B", "KB", "MB", "GB", "TB")
        var unitIndex = 0
        var sizeInDouble = size.toDouble()

        while (sizeInDouble >= 1024 && unitIndex < units.size - 1) {
            sizeInDouble /= 1024.0
            unitIndex++
        }
        return String.format("%.2f %s", sizeInDouble, units[unitIndex])
    }

    @SuppressLint("DiscouragedPrivateApi")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i("MainActivity", "onCreate")
    
        val totalSpace: Long = getAllStorage(this)
        val freeSpace: Long = getFreeStorage(this)

        Log.d("MainActivity", "Total space:" + formatSize(totalSpase))
        Log.d("MainActivity", "Free space:" + formatSize(freeSpace))
    }
}

はい!これでバッチリ!!!……だと思うじゃないですか
これが苦悩の始まりでした

ストレージサイズが違う

そうして出てきたサイズはこちら

MainActivity            com.example.storagesample            I  onCreate
MainActivity            com.example.storagesample            D  Total space:461.29 GB
MainActivity            com.example.storagesample            D  Free space:317.18 GB

そしてスマホの設定アプリで確認したスクショ



んでそれらをまとめた表が↓(小数点第一位を四捨五入)
全体容量 空き容量
取得した値 461 GB 317 GB
設定アプリの値 512 GB 341 GB

う〜ん
ぜんっっっっぜん違うな!なんだこりゃ

原因について考えよう

こういう時はひとまずリファレンスを見直す…!

う〜ん言及なし!
強いていうなら”システムファイルがサポートしてる”といったところでしょうか

続いてStackOverFlowにあたってみると、それっぽいのが…!!

先ほどのコードを変更して

    //内部ストレージの全体容量
    fun getInternalAllStorageVolume(): Long {
        val statFs = StatFs(context.filesDir.absolutePath)
        val storagemanager = getSystemService(StorageManager::class.java)
        val path = storagemanager.storageVolumes[0].directory?.absolutePath
        Log.d (TAG, "statFs: $path")
        val totalBytes = statFs.totalBytes
        return totalBytes
    }

としてみると

ContentValues           com.example.storagesample            D  statFs: /storage/emulated/0

といったログが取れる
これが参照しているストレージのパス。
/storage/emulated/の値をとっていることがわかったので、
adb shell df -h
でROMの容量を見てみると

/dev/fuse         461G 144G  317G  32% /storage/emulated

と出る
つまり取得した値自体は間違ってなくて、一つのパーティション(?)のみを見てたからダメだったわけだ

じゃあ全部の場所を示してやればうまくいくってことかー!
よーし!

Filesystem        Size Used Avail Use% Mounted on
/dev/block/dm-10  3.6G 3.6G     0 100% /
tmpfs             7.4G 2.1M  7.4G   1% /dev
tmpfs             7.4G    0  7.4G   0% /mnt
/dev/block/dm-11  405M 404M     0 100% /system_ext
/dev/block/dm-12  1.7G 1.6G     0 100% /product
/dev/block/dm-13  1.6G 1.6G     0 100% /vendor
/dev/block/dm-14   62M  62M     0 100% /vendor_dlkm
/dev/block/dm-16  1.3M 1.3M     0 100% /odm
tmpfs             7.4G  96K  7.4G   1% /apex
/dev/block/sda9   372M  64M  300M  18% /asdf
----下にめっちゃ続くーーーーーーーーーーーーーーーーー

無理だなこれ…
何よりもいろんなデバイスに入ること想定したら、全てのデバイスに通用するやり口があるのかもわからん。実際、上記のStackOverFlowの回答者のやり方は、俺のデバイスではうまくいかず481GBになった。

There is no means of doing that, except perhaps on a rooted device.
とかも書かれてるし無理なのかなー

望んでいた取得方法

そんな感じで色々ためしつつ調べてを繰り返してたら、↓を発見

これによるとandroid.os.StatFsではなく、android.app.usage.StorageStatsManagerを使えばうまくいくよとのこと…!

リファレンスみてみると
スクリーンショット 2024-09-12 20.39.28.png
わー!これですこれです!
僕が知りたかった内容です!


ということでまたまたコードをポチポチして

class MainActivity : AppCompatActivity() {

    //内部ストレージの全体容量
    fun getAllStorage(context: Context): Long {
        val storage:StorageStatsManager = context.getSystemService(StorageStatsManager::class.java)
        try {
            val totalBytes = storage.getTotalBytes(StorageManager.UUID_DEFAULT)
            return  totalBytes
        }catch (e: IOException) {
            e.printStackTrace()
            return 0
        }
    }

    //内部ストレージの空き容量
    fun getFreeStorage(context: Context): Long {
        val storage:StorageStatsManager = context.getSystemService(StorageStatsManager::class.java)
        try {
            val totalBytes = storage.getFreeBytes(StorageManager.UUID_DEFAULT)
            return  totalBytes
        }catch (e: IOException) {
            e.printStackTrace()
            return 0
        }
    }

    // バイト単位のサイズを読みやすい形式に変換するヘルパー関数
    private fun formatSize(size: Long): String {
        if (size <= 0) return "0 B"
        
        val units = arrayOf("B", "KB", "MB", "GB", "TB")
        var unitIndex = 0
        var sizeInDouble = size.toDouble()

        while (sizeInDouble >= 1024 && unitIndex < units.size - 1) {
            sizeInDouble /= 1024.0
            unitIndex++
        }
        return String.format("%.2f %s", sizeInDouble, units[unitIndex])
    }

    @SuppressLint("DiscouragedPrivateApi")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i("MainActivity", "onCreate")
    
        val totalSpace: Long = getAllStorage(this)
        val freeSpace: Long = getFreeStorage(this)

        Log.d("MainActivity", "Total space:" + formatSize(totalSpace))
        Log.d("MainActivity", "Free space:" + formatSize(freeSpace))
    }
}
MainActivity            com.example.storagesample            I  onCreate
MainActivity            com.example.storagesample            D  Total space:476.84 GB
MainActivity            com.example.storagesample            D  Free space:317.30 GB

???数値が変だぞ
なんでだー!

↑の記事によるとアプリは1KBを1000Bで計算してるってこと…?
実は「1000Bで計算してるんやでー」って記述、別のブログでも度々見かけてた
そこでformatSizeの値を1024から1000に変えてみると

MainActivity            com.example.storagesample            D  Total space:512.00 GB
MainActivity            com.example.storagesample            D  Free space:340.92 GB

おお!設定アプリで見られる値と同じ!

これにて成功!

でも何で設定アプリでは1KBを1000Bとしてるのか。
また、「設定アプリでは1KBを1000Bとしている」と言える根拠となるコードも見てないので、おいおい探していきたい
(知っている方がいらしゃったら教えて欲しい!)


twitter:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0