我知道这个问题有点老了,但是这里有一个可行的解决方案,可以在除了根目录之外的任何地方组织您的文件。
首先,在您的build.gradle文件中,实现SAF框架的DocumentFile类:
implementation 'androidx.documentfile:documentfile:1.0.1'
接下来调用此方法请求权限以使 SAF 正常运行(用户安装后只需执行一次):
private void requestDocumentTreePermissions() {
new AlertDialog.Builder(this)
.setMessage("*Please Select A Folder For The App To Organize The Videos*")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public void onClick(DialogInterface dialog, int which) {
StorageManager sm = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
Intent intent = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
String startDir = "Documents";
Uri uri = intent.getParcelableExtra("android.provider.extra.INITIAL_URI");
String scheme = uri.toString();
scheme = scheme.replace("/root/", "/document/");
scheme += "%3A" + startDir;
uri = Uri.parse(scheme);
Uri rootUri = DocumentsContract.buildDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
uri.toString()
);
Uri treeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
uri.toString()
);
uri = Uri.parse(scheme);
Uri treeUri2 = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
uri.toString()
);
List<Uri> uriTreeList = new ArrayList<>();
uriTreeList.add(treeUri);
uriTreeList.add(treeUri2);
getPrimaryVolume().createOpenDocumentTreeIntent()
.putExtra(EXTRA_INITIAL_URI, rootUri);
Intent intent2 = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent2.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
intent2.putExtra(EXTRA_INITIAL_URI, rootUri);
startActivityForResult(intent2, 99);
}
})
.setCancelable(false)
.show();
}
接下来存储一些持久化权限:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 99 && resultCode == RESULT_OK) {
Uri uri = data.getData();
getSharedPreferences().edit().putString(ACCESS_FOLDER, uri.toString()).apply();
final int takeFlags = (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getApplicationContext().getContentResolver().takePersistableUriPermission(uri, takeFlags);
recreate();
}
}
您可以在正确的文件上调用documentFile的rename方法。
DocumentFile df = DocumentFile.fromTreeUri(MainActivity.this, uri);
df = df.findFile("CurrentName")
df.renameTo("NewName");
您还可以使用内容解析器打开InputStreams和OutputStreams,因为该DocumentFile的持久URI权限已授予您的内容解析器,使用以下代码片段:
getContentResolver().openInputStream(df.getUri());
getContentResolver().openOutputStream(df.getUri());
InputStreams 用于读取,OutputStreams 用于保存
您可以使用以下命令列出文件
df.listFiles();
或者您可以使用以下命令列出文件:
public static DocumentFile findFileInDirectoryMatchingName(Context mContext, Uri mUri, String name) {
final ContentResolver resolver = mContext.getContentResolver();
final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(mUri,
DocumentsContract.getDocumentId(mUri));
Cursor c = null;
try {
c = resolver.query(childrenUri, new String[]{
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_LAST_MODIFIED
}, DocumentsContract.Document.COLUMN_DISPLAY_NAME + " LIKE '?%'", new String[]{name}, null);
c.moveToFirst();
while (!c.isAfterLast()) {
final String filename = c.getString(1);
final String mimeType = c.getString(2);
final Long lastModified = c.getLong(3);
if (filename.contains(name)) {
final String documentId = c.getString(0);
final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(mUri,
documentId);
return DocumentFile.fromTreeUri(mContext, documentUri);
}
c.moveToNext();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (c != null) {
c.close();
}
}
return null;
}
哪种方法比df.listFiles()方法运行更快
源代码(这是我自己的实现,但这里是原始的SF问题)
在针对Android 11(Api 30)时重命名视频/图像