Android 6(Marshmallow)中从图库获取图片

10
在我的应用程序中,我试图从相册中选择图片,以便将该图片传递到服务器。在Android 5及以下版本上,代码运行良好,但是在Nexus 5上的Android 6上,我无法获取图像信息。我得到的日志跟踪信息如下
注意:代码在Android 5及以下版本上正常工作。
11-06 12:27:43.736: W/System.err(31678): java.lang.SecurityException: Permission Denial: reading com.google.android.apps.photos.contentprovider.MediaContentProvider uri content://com.google.android.apps.photos.contentprovider/0/1/content%3A//media/external/images/media/19138/ACTUAL/94710853 from pid=31678, uid=10111 requires the provider be exported, or grantUriPermission()
11-06 12:27:43.757: W/System.err(31678): 
at android.os.Parcel.readException(Parcel.java:1599)
11-06 12:27:43.757: W/System.err(31678): 
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
11-06 12:27:43.757: W/System.err(31678): 
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
11-06 12:27:43.757: W/System.err(31678): 
at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
11-06 12:27:43.757: W/System.err(31678): 
at android.content.ContentResolver.query(ContentResolver.java:491)
11-06 12:27:43.757: W/System.err(31678): 
at android.content.ContentResolver.query(ContentResolver.java:434)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.ContentFilesystem.openCursorForURL(ContentFilesystem.java:258)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.ContentFilesystem.getFileMetadataForLocalURL(ContentFilesystem.java:169)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.FileUtils.getFileMetadata(FileUtils.java:822)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.FileUtils.access$500(FileUtils.java:52)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.FileUtils$15.run(FileUtils.java:394)
11-06 12:27:43.758: W/System.err(31678): 
at org.apache.cordova.file.FileUtils$25.run(FileUtils.java:551)
11-06 12:27:43.758: W/System.err(31678): 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
11-06 12:27:43.758: W/System.err(31678): 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
11-06 12:27:43.758: W/System.err(31678): 
at java.lang.Thread.run(Thread.java:818)

不错的解释:http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en - AEMLoviji
我猜你应该尝试在同一上下文中解码图像,而不是将URI传递到另一个上下文并在那里解码图像(例如startActivity)。 - Yugy
5个回答

22

按照以下方式操作... 在按钮单击时检查SDK版本,使用我从http://web.archive.org/web/20160111030837/http://developer.android.com/training/permissions/requesting.html复制的代码。

 if (Build.VERSION.SDK_INT >= 23){
    // Here, thisActivity is the current activity
                        if (ContextCompat.checkSelfPermission(MainActivity.this,
                                Manifest.permission.READ_EXTERNAL_STORAGE)
                                != PackageManager.PERMISSION_GRANTED) {
    
                            // Should we show an explanation?
                            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                                    Manifest.permission.READ_EXTERNAL_STORAGE)) {
    
                                // Show an expanation to the user *asynchronously* -- don't block
                                // this thread waiting for the user's response! After the user
                                // sees the explanation, try again to request the permission.
    
                            } else {
    
                                // No explanation needed, we can request the permission.
    
                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                                        MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
    
                                // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
                                // app-defined int constant. The callback method gets the
                                // result of the request.
                            }
                        }else{
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                                    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
                        }
                    }else {
    
                        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
                        photoPickerIntent.setType("image/*");
                        startActivityForResult(photoPickerIntent, SELECT_PHOTO);
                    }

然后在覆盖方法onRequestPermissionsResult中编写以下代码:

 case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                    Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
                    photoPickerIntent.setType("image/*");
                    startActivityForResult(photoPickerIntent, SELECT_PHOTO);
                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }

之后

@Override
    protected void onActivityResult(int reqCode, int resultCode, Intent data) {
        super.onActivityResult(reqCode, resultCode, data);

        switch (reqCode) {  case SELECT_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        final Uri imageUri = data.getData();
                        final InputStream imageStream = getContentResolver().openInputStream(imageUri);
                        final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
                        contactimage.setImageBitmap(selectedImage);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }

                }

1
MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE是什么,我应该在哪里初始化它? - Mahmudur Rahman
这是一个静态整数值,请将其分配给任何整数值,但该值不应用于任何其他请求。例如,如果您正在使用值1来表示相机,则对于此情况,您可以使用除1以外的任何整数值。 - anon

5
我相信您需要在运行时向用户请求权限(这是从API 22到23最明显的变化之一)。
您可以尝试使用此片段,该片段提取自Android开发人员权限页面
if (Build.VERSION.SDK_INT >= 23){    
// Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
                    Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {

        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_EXTERNAL_STORAGE)) {

            // Show an expanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.

        } else {

            // No explanation needed, we can request the permission.

            ActivityCompat.requestPermissions(thisActivity,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);

            // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    }
  }

如果有帮助,请告诉我。


2

我遇到了同样的问题。在从图库中选择图片后,在ImageView中显示该图片,使用以下代码:

Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(galleryIntent, videoIdentifier);

并从URI中解码位图图像

但是保存URI以备将来使用,重新打开应用程序并重复使用保存的URI未能奏效。无法从URI解码位图。

我通过从URI路径提取原始内容URI来解决了这个问题。

public String RemoveUnwantedString(String pathUri){
    //pathUri = "content://com.google.android.apps.photos.contentprovider/-1/2/content://media/external/video/media/5213/ORIGINAL/NONE/2106970034"
    String[] d1 = pathUri.split("content://");
    for (String item1:d1) {
        if (item1.contains("media/")) {
            String[] d2 = item1.split("/ORIGINAL/");
            for (String item2:d2) {
                if (item2.contains("media/")) {
                    pathUri = "content://" + item2;
                    break;
                }
            }
            break;
        }
    }
    //pathUri = "content://media/external/video/media/5213"
    return pathUri;
}

2
为了在Marshmallow中从相册中获取图片,您需要在运行时授予READ_EXTERNAL_STORAGE权限。Android M版本已经改变了应用程序被授权的方式。
在Marshmallow之前,所有必需的权限都会一次性被授予,并且是在安装时进行授权的。但是在Marshmallow中,仅在需要该权限时才会要求用户授权,并且用户必须允许或拒绝该权限。
以下两个链接将为您提供更多帮助。链接1来自Android官方文档,链接2是一个博客,使用工作示例解释了这个问题。
链接1:http://developer.android.com/training/permissions/requesting.html 链接2:http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en

0

试一下

这是我创建的简单方法,其中包括其他例子。

在需要的地方调用CHECKGALLERYPERMISSION()方法,并将代码粘贴到该方法中。 private void CHECKGALLERYPERMISSION() { int MY_READ_PERMISSION_REQUEST_CODE =1 ; int PICK_IMAGE_REQUEST = 2; if (ContextCompat.checkSelfPermission(YourActivity.this, android.Manifest.permission.READ_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, "选择图片"), PICK_IMAGE_REQUEST); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions( new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, MY_READ_PERMISSION_REQUEST_CODE); } else { } } }
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == MY_READ_PERMISSION_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, "选择图片"), PICK_IMAGE_REQUEST); } }
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) { Uri uri = data.getData(); try { Bitmap bitmap = MediaStore.Images.Media.getBitmap(YourActivity.this.getContentResolver(), uri); imageview.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } } }

注意

  1. 在片段中使用getactivity()而不是activity.this
  2. 如果android.Manifest.permission.READ_EXTERNAL_STORAGE无法正常工作,请将其更改为Manifest.permission.READ_EXTERNAL_STORAGE,并在所有位置中进行更改

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