Intereting Posts
Как показать эллипсы в моем TextView, если он больше, чем 1 строка? Как проверить, является ли мое приложение стандартной пусковой установкой Мне нужно вызвать HttpURLConnection.disconnect после окончания использования Поддерживаемые значения @SuppressWarnings в Android Studio Javax.net.ssl.SSLException: SSL-квитирование отменено. Сброс соединения с помощью одноранговой сети при вызове webservice Android. DatePicker показывает неправильное значение месяца Bluetooth Low Energy для Android эмулятора Java.lang.OutOfMemoryError для хранения изображений в sqlite db FragmentStatePagerAdapter перестает работать после обновления до ADT 22? Отключить мягкую клавиатуру на NumberPicker Как реализовать как ontouch, так и onfling в одном списке? Для производительности важно, чтобы ViewHolder был статичным в шаблоне ViewHolder? Как получить выбранные элементы из списка Multi Select List Что именно делает метод Activity.finish ()? Как загрузить AnimationDrawable из xml-файла

Mkdir () работает во внутренней памяти флеш-памяти, но не SD-карта?

В настоящее время я создаю приложение для управления файлами, которое позволяет пользователю просматривать файловую систему своего устройства. Пользователь запускается в корневом каталоге / своего устройства, но может просматривать любое место, которое они хотят, например, внутреннюю флеш-память или SD-карту.

Одним из важнейших требований этого приложения является предоставление пользователю возможности создавать новые папки в любом месте. Функция, подобная этой, была бы очень полезной для приложения. Однако метод File#mkdir() не работает вообще в каталоге SD-карты.

Я добавил соответствующие разрешения для файла манифеста. Я также написал тест, чтобы узнать, какие каталоги (все из которых существуют на моем устройстве Lollipop 5.0) позволяют создать новую папку. Из моих наблюдений File#mkdir() работает только во внутреннем каталоге флэш-памяти.

Примечание: пожалуйста, не путайте Environment#getExternalStorageDirectory() с местоположением SD-карты, как объясняется в этой статье . Также на Lollipop 5.0 я верю /storage/emulated/0/ и /storage/sdcard0/ ссылаюсь на внутреннюю флеш-память в то время как /storage/emulated/1/ and /storage/sdcard1/ ссылается на SD-карту (которая не менее True для устройства, с которым я тестирую).

Как я могу создавать новые файлы и папки в областях, находящихся за пределами внешнего пути хранилища на устройствах без привязки к Android?


Manifest:

 ... <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... 

Контрольная работа:

 ... public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final String NEW_FOLDER_NAME = "TestFolder"; testPath(new File(Environment.getExternalStorageDirectory(), NEW_FOLDER_NAME)); testPath(new File("/storage/emulated/0/", NEW_FOLDER_NAME)); testPath(new File("/storage/emulated/1/", NEW_FOLDER_NAME)); testPath(new File("/storage/sdcard0/Download/", NEW_FOLDER_NAME)); testPath(new File("/storage/sdcard1/Pictures/", NEW_FOLDER_NAME)); } private void testPath(File path) { String TAG = "Debug.MainActivity.java"; String FOLDER_CREATION_SUCCESS = " mkdir() success: "; boolean success = path.mkdir(); Log.d(TAG, path.getAbsolutePath() + FOLDER_CREATION_SUCCESS + success); path.delete(); } } 

Вывод:

 /storage/emulated/0/TestFolder mkdir() success: true /storage/emulated/0/TestFolder mkdir() success: true /storage/emulated/1/TestFolder mkdir() success: false /storage/sdcard0/Download/TestFolder mkdir() success: true /storage/sdcard1/Pictures/TestFolder mkdir() success: false 

Solutions Collecting From Web of "Mkdir () работает во внутренней памяти флеш-памяти, но не SD-карта?"

Во-первых, вы должны заметить, что file.mkdir() и file.mkdirs() возвращают false если каталог уже существует. Если вы хотите узнать, существует ли каталог при возврате, используйте (file.mkdir() || file.isDirectory()) или просто проигнорируйте возвращаемое значение и вызовите file.isDirectory() (см. Документацию).

Тем не менее, ваша настоящая проблема заключается в том, что вам нужно разрешение на создание каталога на съемном носителе на Android 5.0+. Работа со съемными SD-картами на Android ужасна.

На Android 4.4 (KitKat) доступ к SD-картам Google ограничен (см. Здесь , здесь и здесь ). См. Этот ответ StackOverflow, который приводит к этому сообщению XDA, если вам нужно создать каталог на съемной SD-карте на Android 4.4 (KitKat).

На Android 5.0 (Lollipop) Google представил новые API доступа к карте SD. Для использования примера обратитесь к этому ответу stackoverflow .

В принципе, вам нужно использовать DocumentFile#createDirectory(String displayName) для создания своего каталога. Перед созданием этого каталога вам нужно попросить пользователя предоставить разрешения для вашего приложения.


ПРИМЕЧАНИЕ. Это относится к съемному хранилищу. Использование File#mkdirs() будет работать на внутреннем хранилище (которое часто путают с внешним хранилищем на Android), если у вас есть разрешение android.permission.WRITE_EXTERNAL_STORAGE .


Я отправлю несколько примеров кода ниже:

Проверьте, нужно ли вам запрашивать разрешение:

 File sdcard = ... // the removable SD card List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions(); DocumentFile documentFile = null; boolean needPermissions = true; for (UriPermission permission : permissions) { if (permission.isWritePermission()) { documentFile = DocumentFile.fromTreeUri(context, permission.getUri()); if (documentFile != null) { if (documentFile.lastModified() == sdcard.lastModified()) { needPermissions = false; break; } } } } 

Далее (если needPermissions true ), вы можете отобразить диалоговое окно, чтобы объяснить пользователю, что им нужно выбрать «SD-карту», ​​чтобы дать вашим приложениям права создавать файлы / каталоги, а затем запустить следующее действие:

 if (needPermissions) { // show a dialog explaining that you need permission to create the directory // here, we will just launch to chooser (what you need to do after showing the dialog) startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), STORAGE_REQUEST_CODE); } else { // we already have permission to write to the removable SD card // use DocumentFile#createDirectory } 

Теперь вам нужно проверить resultCode и onActivityResult в onActivityResult :

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == STORAGE_REQUEST_CODE && resultCode == RESULT_OK) { File sdcard = ... // get the removable SD card boolean needPermissions = true; DocumentFile documentFile = DocumentFile.fromTreeUri(MainActivity.this, data.getData()); if (documentFile != null) { if (documentFile.lastModified() == sdcard.lastModified()) { needPermissions = false; } } if (needPermissions) { // The user didn't select the "SD Card". // You should try the process over again or do something else. } else { // remember this permission grant so we don't need to ask again. getContentResolver().takePersistableUriPermission(data.getData(), Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // Now we can work with DocumentFile and create our directory DocumentFile doc = DocumentFile.fromTreeUri(this, data.getData()); // do stuff... } return; } super.onActivityResult(requestCode, resultCode, data); } 

Это должно дать вам хорошее начало работы с DocumentFile и съемными SD-картами на Android 5.0+. Это может быть PITA.


Кроме того, нет общедоступного API, чтобы получить путь к съемной SD-карте (если она существует). Вы не должны полагаться на hardcoding "/storage/sdcard1" ! В StackOverflow есть несколько сообщений об этом. Во многих решениях используется переменная среды SECONDARY_STORAGE . Ниже приведены два метода, которые вы можете использовать для поиска съемных устройств хранения:

 public static List<File> getRemovabeStorages(Context context) throws Exception { List<File> storages = new ArrayList<>(); Method getService = Class.forName("android.os.ServiceManager") .getDeclaredMethod("getService", String.class); if (!getService.isAccessible()) getService.setAccessible(true); IBinder service = (IBinder) getService.invoke(null, "mount"); Method asInterface = Class.forName("android.os.storage.IMountService$Stub") .getDeclaredMethod("asInterface", IBinder.class); if (!asInterface.isAccessible()) asInterface.setAccessible(true); Object mountService = asInterface.invoke(null, service); Object[] storageVolumes; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { String packageName = context.getPackageName(); int uid = context.getPackageManager().getPackageInfo(packageName, 0).applicationInfo.uid; Method getVolumeList = mountService.getClass().getDeclaredMethod( "getVolumeList", int.class, String.class, int.class); if (!getVolumeList.isAccessible()) getVolumeList.setAccessible(true); storageVolumes = (Object[]) getVolumeList.invoke(mountService, uid, packageName, 0); } else { Method getVolumeList = mountService.getClass().getDeclaredMethod("getVolumeList"); if (!getVolumeList.isAccessible()) getVolumeList.setAccessible(true); storageVolumes = (Object[]) getVolumeList.invoke(mountService, (Object[]) null); } for (Object storageVolume : storageVolumes) { Class<?> cls = storageVolume.getClass(); Method isRemovable = cls.getDeclaredMethod("isRemovable"); if (!isRemovable.isAccessible()) isRemovable.setAccessible(true); if ((boolean) isRemovable.invoke(storageVolume, (Object[]) null)) { Method getState = cls.getDeclaredMethod("getState"); if (!getState.isAccessible()) getState.setAccessible(true); String state = (String) getState.invoke(storageVolume, (Object[]) null); if (state.equals("mounted")) { Method getPath = cls.getDeclaredMethod("getPath"); if (!getPath.isAccessible()) getPath.setAccessible(true); String path = (String) getPath.invoke(storageVolume, (Object[]) null); storages.add(new File(path)); } } } return storages; } public static File getRemovabeStorageDir(Context context) { try { List<File> storages = getRemovabeStorages(context); if (!storages.isEmpty()) { return storages.get(0); } } catch (Exception ignored) { } final String SECONDARY_STORAGE = System.getenv("SECONDARY_STORAGE"); if (SECONDARY_STORAGE != null) { return new File(SECONDARY_STORAGE.split(":")[0]); } return null; } 

path.mkdir() также не работает, когда каталог уже существует. Вы можете сначала добавить чек:

 if (!path.exists()) { boolean success = path.mkdir(); Log.d(TAG, path.getAbsolutePath() + FOLDER_CREATION_SUCCESS + success); path.delete(); } else { Log.d(TAG, path.getAbsolutePath() + "already exists"); } 

На Kitkat google ограниченный доступ к внешней SD-карте, поэтому вы не сможете писать на внешнем хранилище на Kitkat.

В Lollipop Google создала новую FrameWork для записи данных на внешнее хранилище, и вам нужно использовать новый DocumentFile

Класс, который имеет обратную совместимость.

В принципе, вы можете запросить разрешение на onstart приложения в корневой каталог приложения, а затем вы можете создать каталог

Попробуйте это. Он отлично работает для меня.

 final String NEW_FOLDER_NAME = "TestFolder"; String extStore = System.getenv("EXTERNAL_STORAGE"); File f_exts = new File(extStore, NEW_FOLDER_NAME); String secStore = System.getenv("SECONDARY_STORAGE"); File f_secs = new File(secStore, NEW_FOLDER_NAME); testPath(f_exts); textPath(f_secs); 

И изменить логическое значение в функции testPath следующим образом

 boolean success; if(path.exists()) { // already created success = true; } else { success = path.mkdir(); } 

Если папка уже существует, path.mkdir() возвращает false.

И сделал. !!!

Справка из этого вопрос.