• スポンサードリンク

SAF経由でのファイルアクセスで空き容量の取得を試みる

Android

まえふり

何年前だっけ?Android 4.4(KitKat)以降だと思いますが、内部ストレージや外部ストレージ、プライマリーもセカンダリーもアクセスできるようになるStorage Access Framework(以下SAF)という新しい仕組みが追加になりました。

当初はファイルを1つアクセスするのに毎回パーミッションの要求が必要になるう○こ仕様だったわけですが、Android  5以降ではディレクトリ単位でのパーミッション付与が可能になりパーミッションを取得できたディレクトリとその内部のディレクトリ/ファイルには追加のパーミッション要求をすることなくアクセスできるようになりました。
ここらへんの話は、以前にも端末のSDカードに直接動画を保存した〜い(^o^)/という記事にも書きました。

以前の記事はタイトルの通りSDカードに直接動画を保存するにはどうすりゃいいんじゃ?ということでゴニョゴニョすりゃいいんじゃぁという身もふたもない話を書いてしまったわけですが、Android8(API>=26)以降ではMediaMuxerに新しくFileDescriptorを引数にとるコンストラクタが追加になったので、新しい端末に関しては簡単にSDカードへも直接動画を保存できるようになりました。

SDカードに保存できたけど、それだけで大丈夫かい?

保存すると当然のことながらファイルが出来て空き容量が減っていきます。通常のファイルシステムであれば、例えば読み取りアクセス可能なディレクトリへのパスに対してFileやStatFsクラスを使うことで合計容量や使用可能容量を取得することが出来ます。

  • File#getTotalSpaceやFile#getUsableSpace
  • StatFs#getTotalBytesやStatFs#getAvailableBytes

しかしながら残念なことにDocumentFileにはディレクトリの合計容量や空き容量を取得するメソッドが無いのです。DocumentFileがファイルを指し示しているときにファイルサイズを取得することができるだけです。つまりSDカードに録画できたぁ\(^o^)/と調子に乗っていっぱい保存していると、あるとき突然空き容量がないから書き込めんのんじゃぁと怒られることになります😨
これはSDファイルへの書き込みだけではなく、DocumentFileを使って内蔵ストレージへアクセスしている時も同じことが起こります。

はっきり言ってこのままではアプリに組み込むのはすごぉーく不安です。と言うのもAndroidで動画を保存する場合にはMP4フォーマットを使うことがほとんどだと思いますが、MP4フォーマットでは動画のデータの出力が全て終わって初めてmoovボックス(moovアトムとも)を出力できるのです。しかもこのmoovボックスが完全に出力されない限りMP4ファイルは再生できないのです😨
もちろん5分や10分で空き容量がなくなることは殆ど無いでしょうし、それぐらいの短時間であればもう一度取り直すことも可能な場合が多いでしょう。しかし数十分以上の長時間の録画をしようとする場合、出力途中で空き容量が足りなくなってmoovボックスを書き込めなければたとえそれまで1時間でも2時間でも正常に書き込んでいたデータが全てパーになってしまうのです(´・ω・`)
ですので通常であれば空き容量とファイルサイズを一定時間毎に確認しながら保存してmoovボックスが確実に保存できるようにしないといけません。

しかぁーし、先ほども書いたとおりDocumentFileにはディレクトリの合計容量や空き容量を取得するメソッドが無いのです(しつこい)。DocumentFileが指し示しているディレクトリがたまたまプライマリーストレージや外部(セカンダリース)トレージにあればアクセスするすべもありますが、SDカードのようにSAFの経由でしかアクセスできないければ簡単には空き容量を取得できないのです。

ということでちゃんちゃん(TT)/~~~
では終われないので、DocumentFileでもいくつか前提条件を設けることで空き容量を取得できるようにしてみました。

前提条件

まずは前提条件です。

  1. DocumentFileが指し示しているディレクトリの実体が端末内のローカルファイルシステム上に存在していること
  2. FileまたはStatFsクラスを用いてDocumentFileが指し示しているディレクトリの状態の読み込みが可能であること

DocumentFileが指し示しているディレクトリが端末内のローカルファイルシステム上に存在していること

DocumentFileが指し示すディレクトリやファイルには大きく分けて3種類あります。

  1. DocumentsProvider(ContentProviderの下位クラス)経由でアクセスするローカルファイルシステム上のディレクトリ/ファイル。SDカード上のファイルなど
  2. DocumentFile#fromFile(File)メソッドで作られたローカルファイルシステム上のディレクトリ/ファイル。
  3. DocumentsProvider(ContentProviderの下位クラス)経由でアクセスするローカルファイルシステム以外に存在するディレクトリ/ファイル。オンラインストレージサービスなど

今回は上記1,2を対象にします。オンラインストレージなど3つ目はそれぞれのサービス固有の機能を使って別途取得する必要があり汎用的には作成できないので今回は除外です。

FileまたはStatFsクラスを用いてDocumentFileが指し示しているディレクトリの状態の読み込みが可能であること

最初に書きましたが、DocumentFile自体にディレクトリの空き容量や合計容量を取得するメソッドはありません。API >= 9ではStorageManager/StorageVolumeなるクラスがAPIにありもしや使えるのか?と思いましたがDocumentFileを元にアクセスすることは出来ませんでした(´・ω・`)
となると、なんとかしてDocumentFileが指し示しているディレクトリ/ファイルへのパスを生成してFileまたはStatFsでアクセスしてみる以外に選択肢がないのです。

ということで試してみた

まずはDocumentFileからなんとかしてローカルファイルシステム上の実ファイルへのファイルパスをでっちあげないといけません。以前の記事にもちらっと書きましたが細々追加したのがこちら。

どりゃぁーと。

はっきり言ってもっとスマートな方法があるんじゃないかと思うんだけど…まぁとりあえずAndroid 8.1.0のNexus 6pでもAndroid 5のNexus7(2013)でも動くので勘弁しておくなさい。
#getPathにContextとDocumentFile#getUriを渡して呼び出せば絶対ファイルパス文字列が返ってくる…はずなのでそれをFileまたはStatFsに突っ込めばあとは分かるよね?File#getTotalSpaceやFile#getUsableSpaceあるいは、StatFs#getTotalBytesやStatFs#getAvailableBytesを呼び出せば合計容量と空き容量を取得できます。

元のDocumentFileがSAF経由でアクセスできないディレクトリやファイルを指し示している時にどうなるかは知りませんが、SAF経由でアクセス可能なディレクトリやファイルの場合には、少なくともAndroid 8.1.0までは(ファイルの内容自体の読み書きは出来なくとも)空き容量等を取得可能でした\(^o^)/

SDカードはアクセススピードがわからない(今時Class2やClass4ということは無いでしょうが、実際の書き込み速度は動かしてみないとわかりません)ですし、物理的に引っこ抜かれる可能性もあるのでおすすめではないですが、空き容量が確認できるようになったのでリスクを明示した上で内蔵ストレージに対する容量面でのアドバンテージを選択するというのであればまぁ使ってもいいかなぁ(。・_・。)?

ということで今度こそお疲れ様でした
(^.^)/~~~

« »

  • スポンサードリンク