如何在Android API 19(KitKat)中保留权限?

17

我的应用程序中,我将图像路径存储在SQLite数据库中以便进一步使用。我得到的路径是

content://com.android.providers.media.documents/document/image%3A71964

当我从数据库检索到这个路径并尝试从该路径检索图像时,Android会抛出异常。


java.lang.SecurityException: Permission Denial: opening provider
com.android.providers.media.MediaDocumentsProvider 
from ProcessRecord{42c84ec8 23911:com.gots.gb/u0a248} (pid=23911, uid=10248) 
requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS

根据https://developer.android.com/guide/topics/providers/document-provider.html#permissions,我需要添加下面的代码来持久化权限。

final int takeFlags = intent.getFlags()
        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

当我将这段代码添加到扩展BaseAdapter的ImageAdapter类中时,安卓系统抛出了异常。

08-21 02:14:38.530: W/System.err(24452): java.lang.SecurityException:
No permission grant found for UID 10248 and Uri 
content://com.android.providers.media.documents/document/image:71964

这是我 ImageAdapter 代码的相关部分。

public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView ;


    if (convertView == null){
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new GridView.LayoutParams(185, 185));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);

    }
    else{
        imageView = (ImageView)convertView ;
    }

    InputStream is = null;
    Bitmap bitmap = null ;

    try {

        Log.d(TAG,String.valueOf(list.get(position)));
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        final int takeFlags = intent.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.

        if (Build.VERSION.SDK_INT >= 19){
            mContext.getContentResolver().takePersistableUriPermission(list.get(position), takeFlags);
        }


        is = mContext.getContentResolver().openInputStream(list.get(position));

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;
        bitmap = BitmapFactory.decodeStream(is,null, options);
        is.close();
        imageView.setImageBitmap(bitmap);

        return imageView;

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

        return null;
    }

我做错了什么?谢谢


3
尝试在最初获取 Uri 时调用 takePersistableUriPermission(),而不是在稍后使用 Uri 时调用。此外,请勿在主应用程序线程上调用 ContentResolver,因为您在 getView() 中的一些位置正在这样做。特别是 openInputStream() 和你的 BitmapFactory 工作绝对需要移至后台线程。 - CommonsWare
@CommonsWare 我遇到了这个错误 Requested flags 0x1, but only 0x0 are allowed。我正在尝试使用 ACTION_GET_CONTENT 从图库中检索图像。 - Pdksock
我还没有尝试过这个东西(它在我的待办清单上很高),但我猜测画廊没有为您提供可持续的权限。 - CommonsWare
@Pdksock 更改为:final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - Simon
2个回答

32

我相信我已经解决了它。请求意图:

Intent intent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}else{
    intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.form_pick_photos)), REQUEST_PICK_PHOTO);

onActivityResult

...
// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
    ContentResolver resolver = getActivity().getContentResolver();
    for (Uri uri : images) {
        resolver.takePersistableUriPermission(uri, takeFlags);
    }
}
...

我没有在安装KitKat之前测试过这个,我的手机运行的是5.1,有人可以在旧手机上验证一下吗?


3
非常干净优秀的解决方案。有一点需要注意:Utils.isKitkat() 除非添加附加库,否则无法使用。请改用 Build.VERSION.SDK_INT >= 19 - Erich
补充@Erich的评论,你可以使用Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT来使代码更易读。 - user1689757
我收到一个错误,说UID xxxx和Uri 0 @ content://media/external/video/media/xxxx没有可持久化的权限授予。可能是什么问题?谢谢。 - Kiran Ruth R
你会保留URIs多久?我认为当我在重新安装时使用一个URI时,它就被保留了。 - DariusL
接受这个答案! - Flyview
显示剩余5条评论

0
以下是代码:
// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Utils.isKitkat())
{
    final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
    ContentResolver resolver = getActivity().getContentResolver();
    for (Uri uri : images)
    {
        resolver.takePersistableUriPermission(uri, takeFlags);
    }
}

对我来说运行得很好,但我注意到授予我的应用程序可持久化URI的最大数量限制为128。如果我选择超过128个URI,则会出现错误:

java.lang.SecurityException: Permission Denial: opening provider........

当我尝试处理一张我无法保留权限的图像时,你能想出任何解决方案吗?


1
请不要在答案中提问。如果与问题相关,请在问题下添加评论;否则,请开一个新的问题。 - Evan Frisch
如果您实际上不需要一次性使用所有128个URI,那么或许您应该在不需要的URI上调用ContentResolver.releasePersistableUriPermission()... - snark

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