nder(),requestCode,null,0,0,0,null); } else if(Build.VERSION.SDK_INT==Build.VERSION_code.Q) { 尝试 { //在Android==Q中,如果未被拥有,则会引发可恢复
nder(),requestCode,null,0,0,0,null); } else if(Build.VERSION.SDK_INT==Build.VERSION_code.Q) { 尝试 { //在Android==Q中,如果未被拥有,则会引发可恢复安全性异常。 //对于批处理请求,删除将在失败的“未拥有”位置停止 //文件,所以您可能希望将Android Q中的删除限制为 //一次1个文件,让体验不那么难看。 //幸运的是,这在Android R中得到了解决。 for(最终Uri:uriList) { delete(uri,null,null); } } 捕获(可恢复安全性异常ex) { 最终意图发布者意图=ex.getUserAction() .getActionIntent() .getIntentSender(); //重要提示:仍然需要执行实际删除 //像往常一样,再次使用getContentResolver().delete(…), //在“onActivityResult”回调中,就像在Android Q中一样 //所有这些额外代码都是获得许可所必需的, //因为系统根本不执行任何实际的删除。 //onActivityResult没有目标Uri,因此 //我需要在某处抓住它。 activity.startinentsenderforresult(intent、requestCode、null、0、0、null); } } 其他的 { //与旧API一样 for(最终Uri:uriList) { delete(uri,null,null); } } }nder(),requestCode,null,0,0,0,null); } else if(Build.VERSION.SDK_INT==Build.VERSION_code.Q) { 尝试 { //在Android==Q中,如果未被拥有,则会引发可恢复,android,delete-file,saf,Android,Delete File,Saf,nder(),requestCode,null,0,0,0,null); } else if(Build.VERSION.SDK_INT==Build.VERSION_code.Q) { 尝试 { //在Android==Q中,如果未被拥有,则会引发可恢复安全性异常。 //对于批处理请求,删除将在失败的“未拥有”位置停止 //文件,所以您可能希望将Android Q中的删除限制为 //一次1个文件,让体验不那么难看。 //幸运的是,这在Android R中得到了解决。 for(最终Uri:uri
“那么这个应用程序就失去了以前创建的所有公共文件的所有权”——你是如何创建它们的?苏丹武装部队<代码>媒体商店?还有什么?这些文件是使用MediaStore创建的,通过使用ContentResolver插入一条新记录。卸载应用程序后,Android会将字段MediaStore.MediaColumns.OWNER\u PACKAGE\u NAME设置为Null,因此在重新安装应用程序时,从逻辑上讲,此字段不再具有所有权。好的,但是如何拥有“一组已知的特定文件”?应用程序的安装2如何区分应用程序安装1创建的内容与其他应用程序创建的内容或用户复制到设备上的内容?您无法可靠地派生这些文件的SAF
Uri
值,而且您也没有权限。但是,如果它们是MediaStore
条目,如果您按住WRITE\u EXTERNAL\u STORAGE
,则可以使用MediaStore
本身删除它们。文档引用能够使用read_EXTERNAL_STORAGE
读取内容。我们在所有Android版本中删除文件的方式都是通过MediaStore,使用ContentResolver.delete(uri,…),但在Android Q中,对于未拥有的文件,这种操作失败,不考虑写入/读取权限。这些文件是用我们的产品名称保存到特定文件夹中的图像。通过检查BUCKET\u DISPLAY\u NAME字段是否具有应用程序名称,我们可以知道这些文件以前拥有并可以查询它们。我们还打算在Android Q中检查相对_路径是否也包含名称。当然,这不是防弹的,但由于应用程序名称不常见,甚至带有商标,那么另一个应用程序具有相同名称的可能性实际上是零,而没有考虑到用户可能会使用该名称创建另一个文件夹,但我们想不出任何原因。谢谢回答,安卓Q变得越来越复杂。在我看来,就像Android有一个公共共享面板,一个公共权限提示等等一样,那么在Android Q中有了所有新的文件限制,至少Android团队在系统级实现了某种新的删除提示面板是件好事,这可以使删除文件的操作更加安全、一致,并且在应用程序之间保持一致,因此保持安全性并使开发人员的生活更轻松。如何使用DocumentFile.fromsingeluri
我也设法使用Uri documentUri=MediaStore.getDocumentUri(getActivity(),mediaUri)删除单个文件然后是DocumentFile=DocumentFile.fromSingleUri(getActivity(),documentUri)
然后我就对文档文件调用delete。@VinceVD:如果这样可以删除底层内容,那太好了!在这种情况下,OP有一个内容目录(“文件是用我们的产品名保存到特定文件夹中的图像”),我专注于从目录角度工作。这不会删除文件,只是从MediaStore中删除条目。
val uri: String? = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString()
val where = MediaStore.Audio.Media._ID + "=?"
val selectionArgs = arrayOf(mId)
try {
val deleted = mActivity.contentResolver.delete(Uri.parse(uri), where, selectionArgs)
return deleted >= 0
} catch (securityException: SecurityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val recoverableSecurityException =
securityException as? RecoverableSecurityException
?: throw SecurityException()
val intentSender = recoverableSecurityException.userAction.actionIntent.intentSender
intentSender?.let {
mActivity.startIntentSenderForResult(intentSender, 0, null, 0, 0, 0, null)
}
} else {
throw SecurityException()
}
}
val values = ContentValues().apply {
put(MediaStore.Audio.Media.TITLE, song?.title)
put(MediaStore.MediaColumns.DISPLAY_NAME, song?.title)
put(MediaStore.Audio.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Audio.Media.MIME_TYPE, song?.mimeType)
}
val resolver = mContext.contentResolver
val uri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values)
// Download file to Media Store
uri?.let { mUri ->
resolver.openOutputStream(mUri).use { mOutputStream ->
mOutputStream?.let {
// Download to output stream using the url we just created
}
}
}
public static void delete(final Activity activity, final Uri[] uriList, final int requestCode)
throws SecurityException, IntentSender.SendIntentException, IllegalArgumentException
{
final ContentResolver resolver = activity.getContentResolver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
// WARNING: if the URI isn't a MediaStore Uri and specifically
// only for media files (images, videos, audio), the request
// will throw an IllegalArgumentException, with the message:
// 'All requested items must be referenced by specific ID'
// No need to handle 'onActivityResult' callback, when the system returns
// from the user permission prompt the files will be already deleted.
// Multiple 'owned' and 'not-owned' files can be combined in the
// same batch request. The system will automatically delete them using the
// using the same prompt dialog, making the experience homogeneous.
final List<Uri> list = new ArrayList<>();
Collections.addAll(list, uriList);
final PendingIntent pendingIntent = MediaStore.createDeleteRequest(resolver, list);
activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0, null);
}
else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q)
{
try
{
// In Android == Q a RecoverableSecurityException is thrown if not-owned.
// For a batch request the deletion will stop at the failed not-owned
// file, so you may want to restrict deletion in Android Q to only
// 1 file at a time, to make the experience less ugly.
// Fortunately this gets solved in Android R.
for (final Uri uri : uriList)
{
resolver.delete(uri, null, null);
}
}
catch (RecoverableSecurityException ex)
{
final IntentSender intent = ex.getUserAction()
.getActionIntent()
.getIntentSender();
// IMPORTANT: still need to perform the actual deletion
// as usual, so again getContentResolver().delete(...),
// in your 'onActivityResult' callback, as in Android Q
// all this extra code is necessary 'only' to get the permission,
// as the system doesn't perform any actual deletion at all.
// The onActivityResult doesn't have the target Uri, so you
// need to catch it somewhere.
activity.startIntentSenderForResult(intent, requestCode, null, 0, 0, 0, null);
}
}
else
{
// As usual for older APIs
for (final Uri uri : uriList)
{
resolver.delete(uri, null, null);
}
}
}