如何在 android API 19 (KitKat) 中保持权限?

Posted

技术标签:

【中文标题】如何在 android API 19 (KitKat) 中保持权限?【英文标题】:How to persist permission in android API 19 (KitKat)? 【发布时间】:2014-10-14 09:06:43 【问题描述】:

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

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

当我从数据库中检索此路径并尝试从 android 抛出的该路径中检索图像时

java.lang.SecurityException: Permission Denial: opening provider
com.android.providers.media.MediaDocumentsProvider 
from ProcessRecord42c84ec8 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 android 抛出的 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;
    

我做错了什么? 谢谢

【问题讨论】:

在您最初获得Uri 时尝试调用takePersistableUriPermission(),而不是在您稍后尝试使用Uri 时。此外,请不要在主应用程序线程上调用ContentResolver,就像您在getView() 中的几个地方所做的那样。特别是,openInputStream() 和您的 BitmapFactory 工作肯定需要移至后台线程。 @CommonsWare 我收到此错误Requested flags 0x1, but only 0x0 are allowed 错误。我正在尝试使用 ACTION_GET_CONTENT 从图库中检索图像。 我还没有玩过这些东西(它在我的待办事项清单上很重要),但我猜画廊没有为您提供持久许可。 @Pdksock 改变这个:final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; 【参考方案1】:

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

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);
    

...

我还没有测试过这个 pre-kitkat,我的手机运行的是 5.1,有人可以在旧手机上验证吗?

【讨论】:

非常干净和好的解决方案。有一件事:如果没有其他库,Utils.isKitkat() 将不可用。请改用Build.VERSION.SDK_INT >= 19 是的,该方法对构建版本进行了简单的检查。 添加到@Erich 评论,您可以使用 Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT 使其更具可读性。 我收到一条错误消息,提示没有为 UID xxxx 和 Uri 0 @ content://media/external/video/media/xxxx 找到可持久的权限授予 .. 可能是什么问题?? TIA 保留 URI 的时间有多长?我想当我在重新安装中使用 URI 时我明白了。【参考方案2】:

下面列出的代码,

// 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........

就在我尝试处理我无法为其保留权限的图像时。你能想出什么解决办法吗?

【讨论】:

请不要在答案中提问。如果符合问题,请对问题添加评论,否则打开一个新问题。 如果您实际上并不需要所有 128 个 URI,那么也许您应该在不需要的那些上调用 ContentResolver.releasePersistableUriPermission()...

以上是关于如何在 android API 19 (KitKat) 中保持权限?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 android API 19 (KitKat) 中保持权限?

如何让信任锚在 Android API 级别 16-19 上正常工作

Android:Google Place API queryAutoComplete 在 API 17 - API 19 上提供 ZERO_RESULTS

Android Target API - 如何测试我能走多低?

在 API 19 上渲染矢量资产时 Android 应用程序崩溃在 API 20 以上运行良好

您如何使用 Android Volley API?