AppSeedのアプリ開発ブログ

アプリ開発会社AppSeed(アップシード)開発担当のブログです。iOS、Android、Unity、Cocos2d-xなどアプリ開発関連のTipsや備忘録、アプリ開発に役立つ情報を発信します。

【Android】最近の端末ではSDカードに直接保存できない場合が多いみたい

 

https://cdn-ak.f.st-hatena.com/images/fotolife/h/hmbdyh/20170110/20170110101721.jpg

 

Androidアプリ開発の仕事で、アプリからSDカードヘデータを出力するという要件があったんですが、調査をした感じだと、最近の端末ではSDカードヘ直接保存するのが難しい場合があるようです。

 

 

AndroidアプリでSDカードにデータを保存する処理

 

まず、AndroidアプリでSDカードにデータを保存する方法は以下のような感じです。

 

 


public boolean
saveFile(Context context,String file,String data){

String output = data;
FileOutputStream outputStream;

//SDカードの状態を取得
String sdCardState = Environment.getExternalStorageState();
String sdPath = null;
sdPath = Environment.getExternalStorageDirectory() + "/" + file;

if (sdCardState.equals(Environment.MEDIA_MOUNTED)) {
//SDカードがマウントされている状態
new Util().Log("Environment.MEDIA_MOUNTED");
try {
outputStream = new FileOutputStream(sdPath);
outputStream.write(output.getBytes());
outputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else if (sdCardState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { //SDカードが読取専用の場合
new Util().Log("Environment.MEDIA_MOUNTED_READ_ONLY");
return false;
} else if (sdCardState.equals(Environment.MEDIA_REMOVED)) {//SDカードが挿入されていない場合
new Util().Log("Environment.MEDIA_REMOVED");
return false;
} else if (sdCardState.equals(Environment.MEDIA_UNMOUNTED)) {//SDカードがマウントされていない場合
new Util().Log("Environment.MEDIA_UNMOUNTED");
return false;
} else {//その他の場合
new Util().Log("Environment.OTHER");
return false;
}

}



実機で確認してみると、不思議な現象が発生する。。

 

上記のコードで問題ないと思っていたんですが、実機でアプリからSDカードに保存するはずが、内部ストレージに保存されてしまう現象・SDカードを挿していないのに保存できてしまう現象(内部ストレージに保存される)が発生しました。

 

しかもSDカードを挿していない状態で、「Environment.getExternalStorageState()」で外部ストレージの状態を取得して見ると、「Environment.MEDIA_MOUNTED」が返されるので、SDカードがマウントされている状態という謎の現象。。。

 

AndroidにとってSDカードは外部ストレージの選択肢の一つ

 

AndroidとSDカードの関係性がいまいち理解できていなかったので、この機会に調査してみることにしました。

調べた限りだと、Android側からみると、SDカードは外部ストレージの中の一つであって、アプリから外部ストレージに保存しようとしたとしても、SDカードに保存される保証はないようです。

以下の記事がわかりやすかったです。

 

blog.lciel.jp

 

 

Androidの機種によって内部ストレージに保存されてしまう場合があるらしい。。

 

色々調査した結果、以下の記事を発見しました。

 

m-miya.blog.jp

 

Nexus7などの内蔵ストレージが大きい端末では、外部ストレージに内蔵ストレージも割り当てられているものがあり(ほとんどがそうなのかな?)、さらにSDカードスロットがあるものもあり、SDカードが外部ストレージの一部に割り当てられたりする。で、厄介なのが、外部ストレージがSDカードかそうでないのか、SDカードのマウント先を知るAPIがなかったりする・・・(/system/etc/vold.fstabを開いてごにょごにょするとわかるらしいが)

 

つまり、最近の端末では外部ストレージに内部ストレージを割り当てているもの(エミュレートしているもの)がほとんどらしく、アプリからSDカード(外部ストレージ)に保存しようとしても、Android側でエミュレートした内部ストレージに保存されてしまうということなんだと思います。

 

 

エミュレート状態を解除できる方法が見つからない。。

 

SDカードに保存されない原因が、「内部ストレージを外部ストレージにエミュレートしていること」っていうのがわかって、エミュレートを解除できれば、本来の外部ストレージの状態になるから、アプリからSDカードに直接保存できるんじゃないか?

と考えたんですが、僕の使っている端末(ZenPhone 4)ではエミュレートを解除する方法、データの保存先を切り替える方法が見つかりませんでした。

もし知っている方がいたら教えてください。。。

 

 

現状の最善策

 

要件としては、アプリからSDカードに保存したいということでしたが、上記にあげたような問題があるので、現状考えられる最善策は以下かなーと思います。

 

パターン1.SDカードが挿入されていない場合

  内部ストレージに保存する or SDカードが挿入されていないの表示

 

パターン2.SDカードが挿入されている場合(エミュレートされていない場合)

 外部ストレージに保存する(多分SDカードに保存されるはず?)

 

パターン3.SDカードが挿入されている場合(エミュレートされている場合)

 内部ストレージに保存する or SDカードが挿入されていないの表示

 

Androidアプリから端末から取得できる情報としては、

 

外部ストレージが抜き差し可能なのものなのか?は「Environment.isExternalStorageRemovable()」で取得でき、

 

エミュレートされているものなのか?は

Environment.isExternalStorageEmulated()」で取得できます。

 

なので、エミュレートされていない・かつ抜き差し可能な外部ストレージだったら、おそらくSDカードだから保存できる、

 

エミュレートされている・かつ抜き差しできない外部ストレージだったら多分内部ストレージなので、そのまま内部ストレージに保存するか、SDカードに保存できないエラーメッセージを出すって感じかな。

 

とりあえず、エミュレートしている端末でもSDカードに保存できる方法がないか調査したいと思います。