結構レアケースですが、Androidアプリを作っているときに、ローカルファイルに書き込もうとしたら「java.io.FileNotFoundException: /storage/emulated/0/hoge.apk: open failed: EBUSY (Device or resource busy)」となってしまった場合の対応です。
どれかのプロセスがリソースを握ってしまっている
原因としては、書き込みをしようとしているアプリのプロセス以外で、どれかがファイルをのリソースを握ってしまっていることが考えられます。
そしてそれだけならいいのですが、私のケースではどうやらファイル自体は削除されてしまっていたようで、/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 9999u0_aXX 9999 324 885212 33680 ffffffff 400b2880 S com.android.packageinstaller
# kill 9999
としてプロセスを強制終了。するとhoge.apkのパスにまた書き込みができるようになりました。
Android OSの…というより、多少行儀の悪い流れのプログラムを作ってしまったところが反省点です。しかしAndroidはシェルコマンドが弱いからいろいろもどかしい…