fc2ブログ

戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
06

外部ストレージのパスを取得する(Android 2.2~?)

【関連記事】外部SDカードについてまとめるまえのメモ

Androidでは、ストレージ(SDカードなど)のパスを取得するために Environment.getExternalStorageDirectory() が用意されています。
しかし、ストレージが内部ストレージ(取外不可)と外部ストレージ(取外可能)に別れている場合、内部ストレージを取得します。

外部ストレージを取得する Android API は用意されていません。
そこで開発者は色々な方法を使って、外部ストレージのパスを取得しています。

で、私も色々と試しました。こんな感じに。

×環境変数に登録されているパスを取得する
 ⇒ 環境変数がバラバラ。そもそもHTC端末など、一部の端末は登録されていない。
×環境変数と、一部特殊なパスは直書きで対応する
 ⇒ 種類が多すぎて対応しきれない
×シンボリックリンク参照
 ⇒ 設定されていないデバイスがある(そもそもこんなのに期待しては駄目…)
△マウントポイントから取得する(/proc/mounts)
 ⇒ マウントされていないと表示されない
△システムの設定ファイルから取得する(/system/etc/vold/fstab)
 ⇒ Android2.2 (API 8 FROYO)以降からだが対応できそう(HTC J ISW13HT でのみ確認)
 ⇒ (2013/03/02) GalaxyNexus や一部のカスタムROMには存在しないため△にしました…

/system/etc/vold.fstab については、過去記事の『Android のファイルシステム設定ファイル(Android 2.2~?)』を見てください。

この /system/etc/vold.fstab を使ったファイルパスの取得方法は以下のとおり。
------------------------------------------------------------
2013/01/06 追記
Xperia VL SOL21 の場合、内部ストレージが fuse_mount に定義されているらしいので修正しました。
参考:hiroyuki_tの日記 - Androidで外部ストレージのパスを取得する方法
------------------------------------------------------------
/**
* ストレージのパスを取得する。
* ストレージがマウントされていなくてもパスを返します。
* ※ /system/etc/vold.fstab を参照しているため、Android 2.1 (API 7)以下の場合
* {@link java.io.FileNotFoundException} が発生する恐れがある。
*
* @return ストレージのパス一覧
*/
private List<String> getStoragePaths() {
List<String> mountList = new ArrayList<String>();

Scanner scanner = null;
try {
// マウント情報を取得する
scanner = new Scanner(new FileInputStream(new File(
"/system/etc/vold.fstab")));
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith("dev_mount") || line.startsWith("fuse_mount")) {
// dev_mount または fuse_mount のパスを登録する(同じパスは登録しない)
String path = line.replaceAll("\t", " ").split(" ")[2];
if (!mountList.contains(path))
mountList.add(path);
}
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} finally {
if (scanner != null) {
scanner.close();
}
}

return mountList;
}
Android 2.1 (API 7 ECLAIR_MR1) 以下のエミュレータではファイルが存在しなかったので @TargetApi(Build.VERSION_CODES.FROYO) つけようかと思いましたが辞めておきました。

/system/etc/vold.fstab には、ストレージのパスが全て設定されている(はず)ので、Environment.getExternalStorageDirectory()のパスも含まれています。
不要であれば各自で除外してください。

除外の仕方(例)
// ストレージのパス一覧を取得する
List<String> storageList = getStoragePaths();

// 内部ストレージを除外する
storageList.remove(Environment.getExternalStorageDirectory().getPath());

// Android2.3 (API 9) 以降であれば取りはずし可能かで判断できる
// if (!Environment.isExternalStorageRemovable()) {
// storageList.remove(Environment.getExternalStorageDirectory().getPath());
// }

マウントされているか確認する場合は『デバイスにSDカードがマウントされているかチェックする(2)』で書いたメソッドを使ってチェックしてください。
// ストレージのパス一覧を取得する
List<String> storageList = getStoragePaths();

// マウントされていないパスを除外する
for (int i = 0; i < storageList.size(); i++) {
if (!isMounted(storageList.get(i)))
storageList.remove(i--);
}


ほかに /system/etc/vold.fstab を参照している人はいないかと調べてみたところ
wagic というマルチプラットフォームのカードゲームエンジンでも使われていました!(ほぼ同じことしてます)

https://code.google.com/p/wagic/source/browse/trunk/projects/mtg/Android/src/net/wagic/utils/StorageOptions.java

ゲームエンジンで使われているので信頼性は高そう!(主観)

こちらではパスに対して String#replaceAll(":.*$", "") しています。
必要なデバイスがあるということでしょうか…
とりあえずは無しで置いておきます。
スポンサーサイト




Comments

管理人のみ閲覧できます
このコメントは管理人のみ閲覧できます
Re: 区切り文字について
> vold.fstabの区切り文字がタブの端末が存在する様ですので、
> String path = line.split(" ")[2];
> より前で、タブをスペースに置き換える等の処理を組み込むと、より多くの端末に対応できるかと思います。

との指摘がありましたので修正しました。
ご指摘ありがとうございます!!
感謝です
はじめまして。
この度、自分のスマホを新機種に変えたところ、以前に作ったアプリが外部ストレージ(SDカード)にアクセスできなくなっているのに気づいて、こちらのサイトを参考にさせていただきました。現在、改良中なんですが、getStoragePaths()で取得したStringリストの0番目をpathと使えば大丈夫なのでしょうか??自分の機種(HTC J one)では問題なく動いてるようなのですが、全機種0番目でよいのでしょうか?
理解不足で申し訳ないですが、もしよろしければ教えていただきたく存じます。よろしくお願いいたします。
nobuoさん、始めまして。

こちらで紹介している getStoragePaths() は vold.fstab に記述されている順番でパスが返されるため、外部ストレージが必ず0番目になるとは限りません。
たとえば、HTC J ISW13HT では[0]は内部ストレージで、[1]が外部ストレージ(SDカード)になります。

そのため、取得したパスが内部か外部の判断を行うには、一度リストを for 文などで回して確認する必要があります。
確認方法は記事内の「除外の仕方(例)」を参照してください。
ただし、ストレージが内部と外部に分かれていない場合 Environment.getExternalStorageDirectory() で取得されるパスがSDカードのパスになるのでご注意ください (例:Xperia acro IS11S)

なお、2013/03/02 に追記していますが、vold.fstab が存在しない機種もあります。
△をつけていますが、マウントポイントから取得するのが良いかもしれませんね…。

以下のサイトの方が色々試されているので、一度覗かれることをオススメします。
ソースも公開されているので参考になるかと。

我が征くは肥沃な荒野 - ストレージの扱いが混沌の極みの件
http://zautale.sakura.ne.jp/android/ExternalStorageChecker/index.cgi

--------------------------------------------------
外部ストレージに関しては頭の痛い問題です。
個人的にはSDカードと謳っていなければ Environment.getExternalStorageDirectory() で返されたパスでいいんじゃない?とか思っています。
感謝です
ありがとうございます。

if (!Environment.isExternalStorageRemovable()) {
storageList.remove(Environment.getExternalStorageDirectory().getPath());
}
で、内部にも関わらずgetExternalStorageDirectory()で取得できてしまうpathを除外して、

for (int i = 0; i < storageList.size(); i++) {
if (!isMounted(storageList.get(i)))
storageList.remove(i--);
}
で、マウントされていないpathを除外して、
残ったリストの0番目を使えばいいということでしょうか?

本当にこの仕様は混沌の極みですね。
SDカードと謳いながら、内部ストレージに保存してしまっているアプリって結構あるんじゃないでしょうか・・?
はい、その方法で良いと思います。
ただし、SDカード対応端末でSDカード挿さっていないとパスが取得できないので気をつけてください。

>SDカードと謳いながら、内部ストレージに保存してしまっているアプリって結構あるんじゃないでしょうか・・?

最近のはわかりませんが、1年位前になりますがSDカードと書きつつ内部ストレージに保存していたのをいくつか確認してます。
それもそれなりに有名な会社のゲームでした…。
企業のなので改善されているかもしれないですが、一般のアプリにはまだまだいるかもしれませんね。
>一般のアプリにはまだまだいるかもしれませんね。
そうですね。ここに確実に一人おります。(笑)
けど、お蔭様でなんとかできそうです。本当にありがとうございました。

もしSDカードが刺さっていない状態で、storageListの除外操作を両方ともやってしまったら、リストのsizeが0になっている可能性もあるんですかね?
はい、SDカードが挿さっていなければリストのサイズは0になります。

getStoragePaths() で取得するパスのリストは外部ストレージのみを対象とし、。内部ストレージは除外するようにしています。
(名前だけみると内部も外部も含めているように見えてしまいますが…)
http://nobuo-create.net
おかげさまでバージョンアップ完了しました。本当にありがとうございました。
もしよかったら、防備録としてほとんど誰も見ていないと思われる僕のサイトでアップしたいのですが、ここで教えていただいたgetStoragePaths()メソッド等、そのまま使わせてもらってもよろしいでしょうか?
NGならご遠慮なくおっしゃって下さい。

ありがとうございました。
バージョンアップお疲れ様です。

このサイトの記事をそちらのサイト上にアップするのはOKです。
改良してより良いソースコードにするとなお良しです!
http://nobuo-create.net
ありがとうございます!
なんとも心の広いお言葉!
頑張って、もう一回頭とコードを整理してアップしまっす!

いやぁ、ホントに感謝感謝です。
とむやむくんさんも、頑張ってくださーい。
管理人のみ閲覧できます
このコメントは管理人のみ閲覧できます
大変参考になりました、ありがとうございます。
管理人のみ閲覧できます
このコメントは管理人のみ閲覧できます

Leave a Comment


Body
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始