Androidでファイルを書き込もうとしたときに「open failed: EBUSY (Device or resource busy)」というエラーになってしまう場合の対応

Androidでファイルを書き込もうとしたときに「open failed: EBUSY (Device or resource busy)」というエラーになってしまう場合の対応

CSS

 結構レアケースですが、Androidアプリを作っているときに、ローカルファイルに書き込もうとしたら「java.io.FileNotFoundException: /storage/emulated/0/hoge.apk: open failed: EBUSY (Device or resource busy)」となってしまった場合の対応です。

スポンサーリンク
GoogleAdSence レクタングル(大)

どれかのプロセスがリソースを握ってしまっている

 原因としては、書き込みをしようとしているアプリのプロセス以外で、どれかがファイルをのリソースを握ってしまっていることが考えられます。

 そしてそれだけならいいのですが、私のケースではどうやらファイル自体は削除されてしまっていたようで、/storage/emulated/0/hoge.apkというファイルは存在していませんでした。

 つまりアプリがファイルのリソースを握っているが、そのファイルは削除されることが確定しているためアクセスはできない。でもリソースは握られているので同じパスにファイルを書き込めない。というややこい状態のようでした。

手っ取り早いのは再起動

 対応として手っ取り早いのはとりあえず端末の再起動。

 これによってリソースがいったん解放されるため、まともに動くようになるはず。

 ただ、プログラムの作りが同じだとまた似たようなことになるので、ファイル操作周りの見直しは必要だと思います。

調査をしてみる場合 (要root)

 具体的にどのプロセスが握っているかを調査してみる場合。

 これを正確に追っていくにはroot権限が必要なようです。まずadb rootでrootシェルを使えるようにしてからadb shellを実行し、shellでログインします。(この時点でできない場合が大半だと思います…どうしてrootが必要なのかというところですが、通常ユーザーではlsofコマンドで該当のファイルパスが出てきませんでした)

 シェルからlsofを実行すると、

# lsof
com.andro 9999 u0_aXX 52 ??? ??? ??? ??? /storage/emulated/0/hoge.apk (deleted)

という感じで、どのファイルがどのプロセスに使用されているかというのが出てきます。

「9999」がプロセス番号、u0_aXXが実行ユーザーです。

 この情報を元に、psをしてやると

# ps | grep 9999
u0_aXX    9999  324   885212 33680 ffffffff 400b2880 S com.android.packageinstaller
ということで、パッケージインストーラーが握っていたようでした。
 
 hoge.apkはアプリからインストールさせようとしたapkファイルだったのですが、ダウンロードしてきて一時的に保存してインストーラを起動し、終わったら削除、という手順を踏んでいたところ、アプリのほうから削除してもパッケージインストーラーのほうでは解放していなかったという状況になっていたようです。
 
 ここまで分かれば、

# kill 9999

としてプロセスを強制終了。するとhoge.apkのパスにまた書き込みができるようになりました。

 Android OSの…というより、多少行儀の悪い流れのプログラムを作ってしまったところが反省点です。しかしAndroidはシェルコマンドが弱いからいろいろもどかしい…