Ответ 1
Во-первых, вы должны заметить, что file.mkdir()
и file.mkdirs()
возвращает false
, если каталог уже существует. Если вы хотите узнать, существует ли каталог при возврате, используйте (file.mkdir() || file.isDirectory())
или просто игнорируйте возвращаемое значение и вызывайте file.isDirectory()
(см. Документацию).
Тем не менее, ваша настоящая проблема в том, что вам нужно разрешение на создание каталога на съемном носителе на Android 5.0+. Работа со съемными SD-картами на Android ужасна.
В Android 4.4 (KitKat) ограниченный доступ к SD-картам Google (см. здесь, здесь, и здесь). См. Этот fooobar.com/questions/59969/..., который приводит к этому сообщению XDA, если вам нужно создать на съемной SD-карте на Android 4.4 (KitKat).
В Android 5.0 (Lollipop) Google представил новые API доступа к карте SD. Для использования примера см. Этот fooobar.com/questions/59969/....
В принципе, для создания каталога вам нужно использовать 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
и requestCode
на 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;
}