SDカードはAndroidではリムーバブル ストレージとして外部ストレージの一部として規定されています。
ストレージ オプション | Android Developers
SDカードの書き込み・読み出し
SDカードに限らず、ストレージ領域のファイルをPATHを指定しての書き込み読み出しは昔はできましたが、API29からはできなくなりました。
SDカードは外部ストレージの1つとして、読み書きの方法としては以下のようなやり方があります。
古い話:
SDカードへのアクセスについては、メジャーアップデート毎に仕様が変更されているのが実情でした。Android 2.xからAndroid 9までサポートしたいと言う場合には悲惨なことになります。
Android 7.0ではこのような方法で可能なようです。
特定のディレクトリへのアクセス
KitkatからNougatまではこの方法でできるようです。
How to get SD_Card path in android6.0 programmatically
Android 8.0では
Starting in Android O, the Storage Access Framework allows custom documents providers to create seekable file descriptors for files residing in a remote data source…
Ref: Universal way to write to external SD card on Android
Storage Access Framework を使うということでしょうか
以前は外部ストレージのpathはとりあえず
Environment.getExternalStorageDirectory().getPath()
で取得できてはいましたが、API 29からは使えなくなりました。
以下昔のSDカードのやり方など
Sdカードへの読み書きのヒストリーは
- 当初はExternal Storageとして書き込みは通常で可能
- JELLY_BEAN:これ以下では
- どこにマウントされているか調べればSDカードの書き込める、端末依存
- External Storageは内部メモリー領域を指す
- KitKat: 書き込み完全不可
- Lollipop: Storage Access FrameWorkにより可能となる
Android 5.0 ではストレージ アクセス フレームワークが拡張されて、再度SDカードのデータ読み書きができるようになりました。
とても厄介なのは、バージョン毎に異なる手法を取らざる得なくなり、正直SDカードで云々というのは避けたいところです。最近ではSDカードスロットが無いものもある
以下古い記事、使うこともあるかもしれませんので
Kitkat以降は完全にSDカードの書き込みは使えない
以前のExternal Storage Technical Information によれば
…Apps must not be allowed to write to secondary external storage devices …
このsecondary external storageがSDカードを指しているのでしょう
しかし、ダメだと言われるとかえってなんとかしたくなるのが人情
http://androplus.kagome-kagome.com/Entry/148/
/etc/permissions/platform.xml を編集すれば使えるとか
ただし、要root なので…
こちらは隠しメソッドの getVolumeList() を使うらしいですが
http://qiita.com/aMasatoYui/items/e13664455af45123a66e
結局、世の中 Cloud の流れで、SDカードに無理に保存しようというのが
時代遅れなのかとも思います
JELLY_BEANでのExternal Storage
Androidでは、外部ストレージのパスを取得するために Environment.getExternalStorageDirectory() が用意されている
しかし、ストレージが内部ストレージ(取外不可)と外部ストレージ(取外可能)に別れている場合、
内部ストレージを取得するExternalStorageDirectory といいながら、内部メモリにある、
ユーザーがあたかもSDカードのように自由にアクセスできる領域を指しているだけである
本当の外部にあるストレージ、SDカードのパスを取得する Android API は用意されていない
SDカードのパスを取得するAPIは無い
Google developer: Using the External Storage
In this case, the SD card is not part of the external storage and your app cannot access it (the extra storage is intended only for user-provided media that the system scans).
それでもやはりSDカードの画像データを取り出したい、音楽データがSDにありそれを再生したいというような要望はつきません。何とかできないかと調べてみたら、幾つかトライされているものが、国内外にありました。
Ref: http://inujirushi123.blog.fc2.com/?no=93
システムの設定ファイルから取得する方法です
/system/etc/vold.fstab
システム設定ファイルの中身
#######################
## Regular device mount
##
## Format: dev_mount
## label – Label for the volume
## mount_point – Where the volume will be mounted
## part – Partition # (1 based), or ‘auto’ for first usable partition.
## – List of sysfs paths to source devices
######################
# Mounts the first usable partition of the specified device
dev_mount sdcard /storage/ext_sd auto /devices/platform/msm_sdcc.4/mmc_host
dev_mount usb /storage/usb auto /devices/platform/msm_hsusb_host
「/storage/ext_sd」が、取得するべきSDカードのマウント先(path)
「dev_mount」(機種によっては「fuse_mount」)で始まる行の半角スペース区切りで3番目がpathを示す
というのはどうやら共通の仕様のようだそうです
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
@SuppressLint("NewApi") private String getMount_sd() { // SDカードのマウント先をゲットするメソッド //@TargetApi(9) List<String> mountList = new ArrayList<String>(); String mount_sdcard = null; Scanner scanner = null; try { // システム設定ファイルにアクセス File vold_fstab = new File("/system/etc/vold.fstab"); // マウント情報を取得する scanner = new Scanner(new FileInputStream(vold_fstab)); // 一行ずつ読み込む while (scanner.hasNextLine()) { String line = scanner.nextLine(); // dev_mountまたはfuse_mountで始まる行の if (line.startsWith("dev_mount") || line.startsWith("fuse_mount")) { // 半角スペースではなくタブで区切られている機種もあるらしいので修正して // 半角スペース区切り3つめ(path)を取得 String path = line.replaceAll("t", " ").split(" ")[2]; // 取得したpathを重複しないようにリストに登録 if (!mountList.contains(path)){ mountList.add(path); } } } } catch (FileNotFoundException e) { throw new RuntimeException(e); } finally { if (scanner != null) { scanner.close(); } } // Environment.isExternalStorageRemovable()はGINGERBREAD以降しか使えない if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD){ // getExternalStorageDirectory()が罠であれば、そのpathをリストから除外 // StorageRemovable 取り外し可能な外部ストレージ(つまりSDカード)ならばtrue if (!Environment.isExternalStorageRemovable()) { // mountList.remove(Environment.getExternalStorageDirectory().getPath()); } } // マウントされていないpathは除外 for (int i = 0; i < mountList.size(); i++) { if (!isMounted(mountList.get(i))){ mountList.remove(i--); } } // 除外されずに残ったものがSDカードのマウント先 if(mountList.size() > 0 ){ mount_sdcard = mountList.get(0); } // マウント先をreturn(全て除外された場合はnullをreturn) return mount_sdcard; } // 引数に渡したpathがマウントされているかどうかチェックするメソッド public boolean isMounted(String path) { putLog(debugflag, "Mount Check: " +path); boolean isMounted = false; Scanner scanner = null; try { // マウントポイントを取得する File mounts = new File("/proc/mounts"); // scanner = new Scanner(new FileInputStream(mounts)); // マウントポイントに該当するパスがあるかチェックする while (scanner.hasNextLine()) { if (scanner.nextLine().contains(path)) { // 該当するパスがあればマウントされているってこと isMounted = true; break; } } } catch (FileNotFoundException e) { throw new RuntimeException(e); } finally { if (scanner != null) { scanner.close(); } } // マウント状態をreturn return isMounted; } |
以下は環境変数での検索
Ref: http://inujirushi123.blog.fc2.com/blog-entry-30.html
Android APIには取得する術がないので環境変数からパスを取得
環境変数を呼び出すには System#getenv(String name) を使用
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 |
// 外部ストレージ(SDカード)のパスを取得する。 public static String getExternalStoragePath() { String path; // MOTOROLA 対応 path = System.getenv("EXTERNAL_ALT_STORAGE"); if (path != null){ Log.d(TAG, "EXTERNAL_ALT_STORAGE"); return path; } // Samsung 対応 path = System.getenv("EXTERNAL_STORAGE2"); if (path != null){ Log.d(TAG, "EXTERNAL_STORAGE2"); return path; } // 旧 Samsung + 標準 対応 path = System.getenv("EXTERNAL_STORAGE"); if (path == null){ Log.d(TAG, "EXTERNAL_STORAGE"); path = Environment.getExternalStorageDirectory().getPath(); } // HTC 対応 File file = new File(path + "/ext_sd"); if (file.exists()){ Log.d(TAG, "ext_sd"); path = file.getPath(); } // その他機種 return path; } |
とても厄介です