はじめに
Androidで外部ストレージ(SDカード)のパスを取得する場合、昔であればEnvironment.getExternalStorageDirectory()
が使えました。
しかし、最近ではこのメソッドはほとんどの場合内部ストレージのパスを返すようになってしまい、しかもSDカードのパスは機種によってバラバラという混沌とした状況になっています。
調べてみるとvold.fstabをなめ回すなどの裏技的手法があることがわかりますが、一部の機種では使用できないという情報もあるので、ここでは別の方法を紹介します。
リムーバブルストレージのパスを取得する
肝となるのはStorageManager
が隠し持っているgetVolumeList()
というメソッドです。
このメソッドはStorageVolume
オブジェクトの一覧を返し、StorageVolume
はファイルパスや取り外し可否などの情報を持っています。
非公開のメソッド・クラスにアクセスするので、リフレクションを使用します。
このメソッドはマウントされていないストレージのパスも取得します。
public static String[] getRemovableStoragePaths(Context context) {
List<String> paths = new ArrayList<String>();
try {
StorageManager sm = (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
Method getVolumeList = sm.getClass().getDeclaredMethod("getVolumeList");
Object[] volumeList = (Object[])getVolumeList.invoke(sm);
for (Object volume : volumeList) {
Method getPath = volume.getClass().getDeclaredMethod("getPath");
Method isRemovable = volume.getClass().getDeclaredMethod("isRemovable");
String path = (String)getPath.invoke(volume);
boolean removable = (Boolean)isRemovable.invoke(volume);
if (removable) {
paths.add(path);
}
}
} catch (ClassCastException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return paths.toArray(new String[paths.size()]);
}
メリット
- ICS以降で(たぶん)動く(Honeycombはソースも実機もないので知らん)
- 機種への依存度が低い気がする(未確認のため情報求む)
- リムーバブルかどうかの判断ができるので、誤って内部ストレージを検出する心配がない
- USB機器など複数の外部ストレージがある場合にも対応できる
デメリット
- Gingerbread以前では使えない(
getVolumeList()
が無いと言われる) - 非公開APIなので、将来使えなくなる可能性がある
ちなみにKitKatでは
Context#getExternalFilesDirs(String)
というAPIが追加され、複数の外部ストレージ上のアプリ固有パスを取得できるようになったようです。
KitKat以降ではSDカードへの書き込みが制限されたので、書き込みが目的であればこちらを使ったほうが良さそうです。