アプリでストレージの全体容量を取得したい
ひょんなことで、内部ストレージの全体容量と空き容量を取得する必要性が出てきました
先行調査から「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を使えばうまくいくよとのこと…!
リファレンスみてみると
わー!これですこれです!
僕が知りたかった内容です!
ということでまたまたコードをポチポチして
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: