Po pierwsze, należy pamiętać, że file.mkdir()
i file.mkdirs()
powraca false
jeśli katalog już istnieje. Jeśli chcesz wiedzieć, czy katalog istnieje po powrocie, użyj opcji (file.mkdir() || file.isDirectory())
lub po prostu zignoruj wartość zwracaną i zadzwoń pod numer file.isDirectory()
(patrz dokumentacja).
Mimo to Twoim prawdziwym problemem jest to, że potrzebujesz uprawnień do utworzenia katalogu na wymiennym nośniku w systemie Android 5.0+. Praca z wymiennymi kartami SD na Androida jest przerażająca.
W systemie Android 4.4 (KitKat) Google ograniczył dostęp do kart SD (patrz here, here i here). Zobacz to StackOverflow answer, które prowadzi do tego XDA post, jeśli chcesz utworzyć katalog na wymiennej karcie SD w systemie Android 4.4 (KitKat).
W systemie Android 5.0 (Lollipop) firma Google wprowadziła nowe interfejsy dostępu do kart SD. Aby uzyskać przykładowe użycie, zapoznaj się z tym stackoverflow answer.
Zasadniczo do utworzenia katalogu należy użyć DocumentFile#createDirectory(String displayName)
. Przed utworzeniem tego katalogu musisz poprosić użytkownika o nadanie uprawnień do aplikacji.
UWAGA: Jest pamięci wymiennych. Używanie File#mkdirs()
będzie działać na wewnętrznej pamięci masowej (która często jest mylona z zewnętrzną pamięcią masową w systemie Android), jeśli masz pozwolenie android.permission.WRITE_EXTERNAL_STORAGE
.
będę pisać jakiś przykładowy kod poniżej:
Sprawdź, czy musisz poprosić o pozwolenie:
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;
}
}
}
}
Dalej (jeżeli needPermissions
jest true
), można wyświetlić okno dialogowe aby wyjaśnić użytkownikowi, że musi wybrać "kartę SD", aby dać Twojej aplikacji uprawnienia do tworzenia plików/katalogów, a następnie rozpocząć następującą aktywność:
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
}
Będziesz teraz trzeba sprawdzić resultCode
i requestCode
w 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);
}
To powinno dać dobry start na pracy z DocumentFile
i wymiennych kartach SD na Androidzie 5.0+. Może to być PITA.
Ponadto, nie ma publicznego API, aby uzyskać ścieżkę do wymiennej karcie SD (jeśli w ogóle istnieje). Nie powinieneś polegać na hardcoding "/storage/sdcard1"
! Istnieje sporo postów na ten temat w StackOverflow. Wiele rozwiązań wykorzystuje zmienną środowiskową SECONDARY_STORAGE
. Poniżej przedstawiono dwie metody znalezienia wymiennych urządzeń magazynujących:
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;
}
Nie można zakładać, że/magazynowanie/sdcard lub/magazynowanie/emulowane mapa do niczego. OEM może zmienić nazwy tych elementów na dowolne. –
Czy karta SD jest zamontowana lub tylko do odczytu? – DominicEU
@Gabe Sechan Jestem tego świadomy. Moja aplikacja nie zawiera żadnych założeń, gdzie znajduje się karta SD i wewnętrzny dysk flash, ponieważ ładuje katalog '/ storage /', aby użytkownik mógł wybrać żądany punkt instalacji. – zxgear