I'm writing a method to write files to any sd card on the device.
For example, I get 2 sd card paths for Samsung Galaxy S4 : /storage/emulated/0 is the path for an internal SD - card which is equal to Environment.getExternalStorageDirectory()
and /storage/extSdCard
here's the method to get a list of available SD - card - root paths :
public static String[] getStorageDirectories()
{
// Final set of paths
final Set<String> rv = new HashSet<String>();
// Primary physical SD-CARD (not emulated)
final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
// All Secondary SD-CARDs (all exclude primary) separated by ":"
final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
// Primary emulated SD-CARD
final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
if(TextUtils.isEmpty(rawEmulatedStorageTarget))
{
// Device has physical external storage; use plain paths.
if(TextUtils.isEmpty(rawExternalStorage))
{
// EXTERNAL_STORAGE undefined; falling back to default.
rv.add("/storage/sdcard0");
}
else
{
rv.add(rawExternalStorage);
}
}
else
{
// Device has emulated storage; external storage paths should have
// userId burned into them.
final String rawUserId;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
{
rawUserId = "";
}
else
{
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
final String[] folders = DIR_SEPORATOR.split(path);
final String lastFolder = folders[folders.length - 1];
boolean isDigit = false;
try
{
Integer.valueOf(lastFolder);
isDigit = true;
}
catch(NumberFormatException ignored)
{
}
rawUserId = isDigit ? lastFolder : "";
}
// /storage/emulated/0[1,2,...]
if(TextUtils.isEmpty(rawUserId))
{
rv.add(rawEmulatedStorageTarget);
}
else
{
rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
}
}
// Add all secondary storages
if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
{
// All Secondary SD-CARDs splited into array
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv, rawSecondaryStorages);
}
return rv.toArray(new String[rv.size()]);
}
So, this method return me in my case 2 paths : /storage/emulated/0 and /storage/extSdCard
Then I try to write this file :
public static boolean writeExternal(InputStream source, String filePath) {
Log.d("MyLog", "dest="+filePath);
// собственно запись на флешку
File dest = new File(filePath);
FileOutputStream writer = null;
try {
if (!dest.exists()) {
Log.d("MyLog","creates");
dest.getParentFile().mkdirs();
dest.createNewFile();
}
writer = new FileOutputStream(dest);
writeFromInputToOutput(source, writer);
Log.d("MyLog","writes a file "+filePath);
return true;
}
catch (IOException e) {
e.printStackTrace();
Log.d("MyLog","Error");
return false;
}
finally {
Log.d("MyLog","file written");
close(writer);
}
}
now when I write the file to the first dir which is /storage/emulated/0 - the file creates fine. But the second dir - /storage/extSdCard gives me back an error. here's my stacktrace and log with path to check it (it's the first line):
09-15 01:32:24.124: D/MyLog(26216): dest=/storage/extSdCard/Molitvoslovaudio/morning.mp3
09-15 01:32:24.124: D/MyLog(26216): creates
09-15 01:32:24.124: W/System.err(26216): java.io.IOException: open failed: ENOENT (No such file or directory)
09-15 01:32:24.139: W/System.err(26216): at java.io.File.createNewFile(File.java:946)
09-15 01:32:24.139: W/System.err(26216): at ru.eyescream.prayer.FileMgr.writeExternal(FileMgr.java:156)
09-15 01:32:24.139: W/System.err(26216): at ru.eyescream.prayer.Downloader$Downloading.onPostExecute(Downloader.java:183)
09-15 01:32:24.139: W/System.err(26216): at ru.eyescream.prayer.Downloader$Downloading.onPostExecute(Downloader.java:1)
09-15 01:32:24.139: W/System.err(26216): at android.os.AsyncTask.finish(AsyncTask.java:632)
09-15 01:32:24.139: W/System.err(26216): at android.os.AsyncTask.access$600(AsyncTask.java:177)
09-15 01:32:24.139: W/System.err(26216): at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
09-15 01:32:24.139: W/System.err(26216): at android.os.Handler.dispatchMessage(Handler.java:102)
09-15 01:32:24.139: W/System.err(26216): at android.os.Looper.loop(Looper.java:157)
09-15 01:32:24.139: W/System.err(26216): at android.app.ActivityThread.main(ActivityThread.java:5356)
09-15 01:32:24.139: W/System.err(26216): at java.lang.reflect.Method.invokeNative(Native Method)
09-15 01:32:24.139: W/System.err(26216): at java.lang.reflect.Method.invoke(Method.java:515)
09-15 01:32:24.139: W/System.err(26216): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
09-15 01:32:24.139: W/System.err(26216): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
09-15 01:32:24.139: W/System.err(26216): at dalvik.system.NativeStart.main(Native Method)
09-15 01:32:24.139: W/System.err(26216): Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
09-15 01:32:24.139: W/System.err(26216): at libcore.io.Posix.open(Native Method)
09-15 01:32:24.139: W/System.err(26216): at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
09-15 01:32:24.139: W/System.err(26216): at java.io.File.createNewFile(File.java:939)
09-15 01:32:24.139: W/System.err(26216): ... 14 more
09-15 01:32:24.139: D/MyLog(26216): Error
What's wrong? and how can I fix it?
What's wrong?
You appear to think that Environment.getExternalStorageDirectory()
has something to do with an SD card. That method returns the root of external storage, which is not removable storage (and also is not internal storage, from the standpoint of the SDK).
But, more importantly, the undocumented and unsupported means that script kiddies various people used to access removable storage -- such as what you are trying -- no longer works as of Android 4.4.
I need to reach all of the SD cards on the device
That is no longer possible as of Android 4.4, and was never supported on any version of Android.