首先,您应该注意到
file.mkdir()
和
file.mkdirs()
如果目录已经存在,则返回
false
。如果您想在返回时知道目录是否存在,请使用
(file.mkdir() || file.isDirectory())
或者简单地忽略返回值并调用
file.isDirectory()
(请参阅文档)。
话虽如此,您真正的问题是需要在Android 5.0+上获得在可移动存储中创建目录的权限。在Android上使用可移动SD卡是非常困难的。
在Android 4.4(KitKat)中,Google限制了对SD卡的访问(参见
这里,
这里和
这里)。如果需要在Android 4.4(KitKat)上创建可移动SD卡上的目录,请参阅此
StackOverflow答案,该答案将导向此
XDA帖子。
在Android 5.0(Lollipop)中,Google引入了新的SD卡访问API。有关示例用法,请参阅此
stackoverflow答案。
基本上,你需要使用
DocumentFile#createDirectory(String displayName)
来创建你的目录。在创建此目录之前,你需要要求用户授予你的应用程序权限。
注意: 这是针对可移动存储的。如果您拥有权限android.permission.WRITE_EXTERNAL_STORAGE
,则使用File#mkdirs()
将适用于内部存储(在Android上经常与外部存储混淆)。
下面我将发布一些示例代码:
检查是否需要请求权限:
File sdcard = ...
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) {
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), STORAGE_REQUEST_CODE);
} else {
}
您现在需要在 onActivityResult
中检查 resultCode
和 requestCode
:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == STORAGE_REQUEST_CODE && resultCode == RESULT_OK) {
File sdcard = ...
boolean needPermissions = true;
DocumentFile documentFile = DocumentFile.fromTreeUri(MainActivity.this, data.getData());
if (documentFile != null) {
if (documentFile.lastModified() == sdcard.lastModified()) {
needPermissions = false;
}
}
if (needPermissions) {
} else {
getContentResolver().takePersistableUriPermission(data.getData(),
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
DocumentFile doc = DocumentFile.fromTreeUri(this, data.getData());
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
这应该为您在 Android 5.0+ 上使用 DocumentFile
和可移动 SD 卡提供了一个良好的起点。但这可能有点麻烦。
此外,没有公共API可以获取可移动SD卡的路径(如果存在)。您不应该依赖硬编码
“/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;
}