如何在Android 10 Scoped Storage(Mediastore条目和文件)中删除图像

6

当我尝试删除应用程序贡献/拥有的图像时,可以很容易地删除。但对于我的应用程序未拥有的 Pictures 文件夹中的共享媒体,我通过捕获 RecoverableSecurityException 显示一个提示给用户。但即使是允许,我也无法删除特定的图像文件。

这是我使用的代码,请指出我做错了什么。

  • 图像在删除后不会出现在图库或我的应用程序中,但仍会留在文件管理器中,在手机重新启动后会重新出现在图库中(我猜只有 MediaStore 条目被删除了)
  • 该代码适用于 Android 11 设备。
  • startIntentSenderForResult(intentSender, 12, null, 0, 0, 0, null); 的结果为RESULT_OK

获取文件的代码(此代码位于我的活动中):

   try {
            String DIRECTORY_NAME = "%Pictures/App_Name%";
            String selection = MediaStore.MediaColumns.RELATIVE_PATH + " like ? ";
            String[] selectionArgs = new String[]{DIRECTORY_NAME};
            ContentResolver contentResolver = this.getContentResolver();
            Cursor cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, selection, selectionArgs, null);

            while(cursor.moveToNext()){
                long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
                Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);

                uriArrayList.add(contentUri);

            }
            cursor.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }

删除图像文件:(此代码位于我的适配器类中)

    try {
    ContentResolver contentResolver = context.getContentResolver();
    cachedUri = f; //f is Uri 
    contentResolver.delete(f, null, null);
}catch (SecurityException securityException) {

    RecoverableSecurityException recoverableSecurityException;
    if (securityException instanceof RecoverableSecurityException) {
        recoverableSecurityException =
                (RecoverableSecurityException) securityException;
    } else {
        throw new RuntimeException(
                securityException.getMessage(), securityException);
    }
  
    IntentSender intentSender = recoverableSecurityException.getUserAction()
            .getActionIntent().getIntentSender();
    try {
        resultInterface.onResultTaken(intentSender, cachedUri); //Giving a call to the activity that implements the interface

    } catch (Exception e) {
        e.printStackTrace();
    }
}

提示用户(在我的活动中获取onActivityResult):

public class SomeActivity extends AppCompatActivity implements ResultTakenListener{

protected void onCreate(){ 
...}

@Override
public void onResultTaken(IntentSender intentSender, Uri uri) throws IntentSender.SendIntentException {
    cacheduri = uri;
    startIntentSenderForResult(intentSender, 12, null, 0, 0, 0, null);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == 12){
    Log.d("Result Code is-->", String.valueOf(resultCode)); //This gives RESULT_OK
        if(resultCode == RESULT_OK){
            ContentResolver contentResolver = this.getContentResolver();
            contentResolver.delete(cacheduri, null, null);
    
        }
    }
}}
3个回答

2

好的。有两种方法可以实现。其中一种方法也适用于Android 11。 我这里不是在讨论requestLegacyExternalStorage权限。 顺便说一下,你也可以用那个方法。

在Android 10中,如果你没有使用requestLegacyExternalStorage权限或Storage Access Framework (DocumentsProvider),你必须为每个文件获取删除权限。

为此,请查看以下方法。

对于Android 10:

public static void deleteImageAPI29(Context context, Uri uri) {
    ContentResolver resolver = context.getContentResolver();
    try {
        resolver.delete(uri, null, null);
    } catch (SecurityException securityException) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            RecoverableSecurityException recoverableSecurityException = (RecoverableSecurityException) securityException;
            IntentSenderRequest senderRequest = new IntentSenderRequest.Builder(recoverableSecurityException.getUserAction()
        .getActionIntent().getIntentSender()).build();
            deleteResultLauncher.launch(senderRequest);
        }
    }
}

现在,deleteResultLauncher作为onActivityResult() API的替代品出现,因为它从Android 11开始已经被弃用。
以下是声明方法:
ActivityResultLauncher<IntentSenderRequest> deleteResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == RESULT_OK){
                    Toast.makeText(context, "Image deleted.", Toast.LENGTH_SHORT).show();
                }
            }
        }
);

它将提示一个对话框来删除图片。如果用户允许,那么它将无问题地删除该图片。

针对Android 11:

public void deleteImageAPI30(Context context, ArrayList<Media> arrayList) {
    ContentResolver contentResolver = context.getContentResolver();
    ArrayList<Uri> arrayList2 = new ArrayList();
    for (int i = 0; i < arrayList.size(); i++) {
        arrayList2.add(arrayList.get(i).getUri()); // You need to use the Uri you got using ContentUris.withAppendedId() method
    }
    Collections.addAll(arrayList2);
    IntentSender intentSender = MediaStore.createDeleteRequest(contentResolver, arrayList2).getIntentSender();
    IntentSenderRequest senderRequest = new IntentSenderRequest.Builder(intentSender)
            .setFillInIntent(null)
            .setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 0)
            .build();
    deleteResultLauncher.launch(senderRequest);
}
< p >在此处,deleteResultLauncher的声明与上面所示的相同。

第二种方法更有用,因为它可以一次删除多个图像。但不幸的是,它从Android 11开始。在Android 10中不可用。因此,如果您想在Android 10中删除多个图像,则可以选择requestLegacyExternalStorage选项或'DocumentsProvider' API。

除此之外,这两种方法对于这个问题来说都是完美的。

如果您想使用Mediastore API在Android 10及以上版本中保存图像,请查看本文,它会对您有很大帮助。


1

我没有测试过,但我认为你需要针对版本低于Q的构建时集成自定义行为。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                
         //your existing code that seems to work above Q
            } else {
                String docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
                File file = new File(docsDir, "fileName.jpg");
                if(file.exists());
                file.delete();
            }

如果使用 Android 10 或更高版本,它将无法工作,因为存储 API 已更改。如果在清单文件中声明 requestLegacyExternalStorage,则可以在 Android 10 上运行。但是对于 Android 11,这是不可能的。 - Dev4Life

0
在你的Android清单文件中添加android:requestLegacyExternalStorage="false",这将允许作用域存储。
对于Android 10以下的版本,请将android:requestLegacyExternalStorage设置为false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接