Android 存储访问框架的持久化权限在异步任务期间未被授予。

3
我在这里设置了意图标志:
public void createAlbum(View view) {
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    String intentChooserDialog = getResources().getString(R.string.pick_image_dialog);
    startActivityForResult(Intent.createChooser(intent, intentChooserDialog), PICK_IMAGE);
}

在我的活动结果中,我设置了可持久化权限,如此处所述:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {
        // Get image selection
        if (data != null) {
            ClipData clipData = data.getClipData();
            if (clipData != null) {
                int clipDataCount = clipData.getItemCount();
                if (clipDataCount > 1) {
                    int clipIndex = 0;
                    BitmapUri[] bitmapUris = new BitmapUri[clipDataCount];
                    while (clipIndex < clipDataCount) {
                        ClipData.Item item = clipData.getItemAt(clipIndex);
                        Uri uri = item.getUri();
                        int takeFlags = data.getFlags();
                        takeFlags &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
                        getContentResolver().takePersistableUriPermission(uri, takeFlags);
                        BitmapUri bitmapUri = new BitmapUri(Uri.decode(uri.toString()),            data.getFlags());
                        bitmapUris[clipIndex] = bitmapUri;
                        clipIndex++;
                    }
                    DatabaseAlbumWriter dab = new DatabaseAlbumWriter(this);
                    dab.execute(bitmapUris);
                }
            } else {
                int oneImageSelected = 1;
                BitmapUri[] bitmapUris = new BitmapUri[oneImageSelected];
                Uri uri = data.getData();
                int takeFlags = data.getFlags();
                takeFlags &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
                getContentResolver().takePersistableUriPermission(uri, takeFlags);
                BitmapUri bitmapUri = new BitmapUri(Uri.decode(uri.toString()), data.getFlags());
                bitmapUris[0] = bitmapUri;
                DatabaseAlbumWriter dab = new DatabaseAlbumWriter(this);
                dab.execute(bitmapUris[0]);
            }
        }
    }
}

在将内容样式Uri保存到sqlite3数据库后,我想异步地加载它们到网格视图中,如这里所述。以下是我从培训指南中实现的decodeSampledBitmapFromResource方法。传入一个具有解码后Uri字符串和原始意图的可持久化权限的对象。这些值已在数据库中保存并再次加载。

        protected Bitmap decodeSampledBitmapFromResource(BitmapUri bitmapUri, int reqWidth, int reqHeight) {
        Bitmap bitmap = null;
        Uri uri = Uri.parse(bitmapUri.getStringUri());
        int permissions = bitmapUri.getPermissions();
        permissions &= Intent.FLAG_GRANT_READ_URI_PERMISSION;
        try {
            getContentResolver().takePersistableUriPermission(uri, permissions);
            ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            Rect rect = new Rect(-1,-1,-1,-1);
            BitmapFactory.decodeFileDescriptor(fileDescriptor, rect, options);
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, rect, options);
            parcelFileDescriptor.close();
        } catch (NullPointerException | SecurityException | IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

我在这里遇到了错误:

05-08 14:17:40.850 2728-2813/com.example.app W/System.err: java.lang.SecurityException: No persistable permission grants found for UID 10058 and Uri 0 @ content://com.android.providers.media.documents/document/image:32
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.os.Parcel.readException(Parcel.java:1599)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.os.Parcel.readException(Parcel.java:1552)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.app.ActivityManagerProxy.takePersistableUriPermission(ActivityManagerNative.java:4217)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at android.content.ContentResolver.takePersistableUriPermission(ContentResolver.java:1703)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.decodeSampledBitmapFromResource(AlbumCreator.java:395)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.doInBackground(AlbumCreator.java:343)
05-08 14:17:40.850 2728-2813/com.example.app W/System.err:     at com.example.app.AlbumCreator$BitmapWorkerTask.doInBackground(AlbumCreator.java:329)

如果我使用BitmapFactory.decodeFileDescriptor从parcelFileDescriptor中加载位图,那么在活动结果中就不会出现这个错误。因此,据我所知,AsyncTask是具有不同特权的不同用户。有人知道如何为AsyncTask分配可持久化权限吗?我宁愿不要在UI线程上加载位图。

这是我的清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".AlbumCreator"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!--
             ATTENTION: This was auto-generated to add Google Play services to your project for
             App Indexing.  See https://g.co/AppIndexing/AndroidStudio for more information.
        -->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <activity
            android:name=".AlbumEditor"
            android:label="@string/title_activity_album_editor"
            android:parentActivityName=".AlbumCreator">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.app.AlbumCreator" />
        </activity>
    </application>
</manifest>

请查看这个相关问题和建议的答案 - 希望这也能给你一些启示。 - ishmaelMakitla
我感谢你分享这个相关的问题。然而,我能够像这个部分解决方案中一样使用持久权限加载位图:链接。但是,当我尝试从保存的内容路径外部的方法onActivityResult中加载位图,并且发生在后台进程中时,我就会遇到SecurityException。我猜测这是因为后台进程是不同的用户。或者进程ID不同是一个问题。也许... - LarryBarry
2个回答

5
我解决了自己的问题。如果您查看我的代码,您会发现我使用了intent.getData()。然后,我使用该uri持久化了权限。当我保存uri供以后使用时,我对其进行了解码。Uri.decode(uri.toString())
编码的Uri:content://com.android.providers.media.documents/document/image%3A32 解码后:content://com.android.providers.media.documents/document/image:32 所以,我改用intent.getDataString(),然后使用Uri.parse()创建一个Uri,用于takePersistableUriPermission()方法。然后,我将来自意图的初始数据字符串保存在数据库中,以保留编码的组件。

需要特别注意getDataString(),因为在许多Stack文章中都没有提到它,而且在Android上也省略了细节。谢谢! - Dave Friedel

-1

您需要在清单文件中添加必要的权限。请添加:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

如果您想进行写操作,这是必要的。

试一试,让我们知道是否有帮助。


我添加了这个权限,但仍然收到SecurityException异常。 - LarryBarry

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