在 Android Q 中从外部存储访问照片

Posted

技术标签:

【中文标题】在 Android Q 中从外部存储访问照片【英文标题】:Access photos from external storage in Android Q 【发布时间】:2020-07-19 05:42:10 【问题描述】:

我最近将应用程序的目标版本升级到 API 29。由于 android 10 中的范围存储,我使用 MediaStore API 来存储和检索应用程序外部存储中的图像。之前,我使用getExternalStoragePublicDirectory 存储通过相机拍摄的图像,现在我使用MediaStore.Images.Media.EXTERNAL_CONTENT_URI 将文件写入外部存储位置。

我现在面临的问题是, 当我打开我的应用程序并拍照时,它存储在我给“myapp”的文件夹名称下,我可以通过 Mediastore 光标检索我的图像并将它们显示在自定义画廊中。当我卸载我的应用程序“myapp”文件夹仍然存在。当我再次安装我的应用程序并尝试从图库中读取图像时,光标没有返回任何图像。但是,如果我再次拍照,那么我可以将它们加载到我的自定义画廊中。自定义图库视图只是屏幕底部的一行图像,因此用户无需浏览照片文件夹即可将图像加载到应用程序中。

这就是我在 MediaStore 中存储图像的方式

内容值:

String RELATIVE_PATH = Environment.DIRECTORY_PICTURES + File.separator + "myApp";
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, generateImageName(new Date()));
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, RELATIVE_PATH);

生成名称方法:

int sameSecondCount;
protected String generateName(Date now)
    
        String result = formatter.format(now);

        long nowMillis = now.getTime();
        if (nowMillis / 1000 == lastMillis / 1000)
        
            sameSecondCount++;
            result += "_" + sameSecondCount;
        
        else
            sameSecondCount = 0;

        lastMillis = nowMillis;

        return result + PICTURE_EXTENSION_JPG;
    
@WorkerThread
    private Uri writePictureToFile(ContentValues contentValues, byte[] bitmapBytes) throws IOException
    
        final ContentResolver resolver = getApplication().getContentResolver();

        Uri uri = null;
        final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

        try
        
            uri = resolver.insert(contentUri, contentValues);

            if (uri == null)
                throw new IOException("Failed to create new MediaStore record.");

            OutputStream stream = resolver.openOutputStream(uri);

            if (stream == null)
            
                throw new IOException("Failed to get output stream.");
            

            stream.write(bitmapBytes);
        
        catch (IOException e)
        
            // Delete the content from the media store
            if (uri != null)
                resolver.delete(uri, null, null);
            throw e;
        
        return uri;
     

阅读图片


                String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + " in (?,?,?)";
                String[] args = new String[]
                    MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg"),
                    MimeTypeMap.getSingleton().getMimeTypeFromExtension("png");

                Cursor cursor = context.getContentResolver()
                    .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, selectionMimeType, selectionArgs,
                        orderBy + " DESC");
                if (cursor != null)
                
                    int idColumnIndex = imageCursor.getColumnIndex(MediaStore.Images.Media._ID);
                    imageCursor.moveToFirst();
                    int imageCount = imageCursor.getCount();
                    for (int i = 0; i < imageCount && i < totalCount; i++)
                    
                        final long imageId = imageCursor.getLong(idColumnIndex);
                        Uri uriImage = Uri.withAppendedPath(uriExternal, "" + imageId);
                        GalleryData galleryImageData = new GalleryImageData(imageId, uriImage); // Custom class with id and Uri
                        galleryViewModelList.add(galleryImageData);
                        imageCursor.moveToNext();
                    
                    imageCursor.close();
                

为什么当我重新安装我的应用程序时,上述代码没有返回我存储在 Mediastore 文件夹中的图像。是设计使然还是我遗漏了什么?

这些是我正在检索的列,

final String[] columns =  MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.MIME_TYPE ;
final String orderBy = MediaStore.Images.Media.DATE_TAKEN; ```

【问题讨论】:

如果您有问题或疑问,请告诉并询问。 你应该显示你使用的内容值。 抱歉,格式问题。这是我的问题,“为什么当我重新安装我的应用程序时,上述代码没有返回我存储在 Mediastore 文件夹中的图像?” 您还没有告诉内容值。应该在您的帖子中提出问题。不在评论中。 嗯,帖子中也有问题。刚刚发布在评论中作为对您的回复。 【参考方案1】:

由于不清楚的原因,Android 10 将按时间分开的两个应用安装视为独立的应用,这与两个完全不同的应用没有区别。

因此,一旦您的应用被卸载,它就会失去对其创建的所有文件的访问权限...即使后来重新安装了相同的应用。

因此,您需要像处理完全不相关的应用程序中的文件一样处理以前安装的应用程序中的文件:请求READ_EXTERNAL_STORAGE 以便能够查询它们并读取它们的内容。 “读取其内容”部分需要清单中 &lt;application&gt; 上的 android:requestLegacyExternalStorage="true"

【讨论】:

即使重新安装后,我也可以查询文件的内容,就像我可以查询它的 id 和显示名称一样。但是当我尝试将其转换为位图时,甚至在使用 glide 时,我得到了 file not found 异常,并且我已经授予了 READ_EXTERNAL_STORAGE 权限。在这里确认它仅发生在我的应用程序在重新安装之前创建的文件。你知道为什么会这样吗? @ParagPawar:我澄清了答案——看看这是否解决了你的问题。 但我不应该在 Android 10 及更高版本上使用 requestLegacyExternalStorage 对吧?我会更清楚地解释我的问题。我正在使用 cameraX API 捕获图像并将这些图像存储到特定于应用程序的存储中,但是在存储之后,我正在使用 MediaScannerConnection.scanFile 将其输入到 mediastore,以便厨房应用程序可以访问它。但现在实际问题是,当我卸载应用程序时,它会被删除,但在重新安装时,我仍然会得到它的 mediaStore 条目,但它确实存在于我存储它的存储空间中。但我可以在图库应用程序(照片)中看到它。所以这不应该发生? 当我在内容解析器中查询媒体条目时,我得到了那些应该在卸载我的应用程序时删除的图像的条目。但是当我尝试使用 glide 加载这些条目时,它会给出文件未找到异常。即使我无法将这些图像从照片应用程序共享到 Skype。这意味着即使是 Skype 也无法找到这些图像,但它们怎么会存在于照片应用中呢? @ParagPawar:“但我不应该为 Android 10 及更高版本使用 requestLegacyExternalStorage 对吧?” - 那里的建议已经改变。在 Android 10 上,这是可以安全使用的,因为即使在您的 targetSdkVersion 达到 29 之后,他们也允许使用它。在 Android 11 上,READ_EXTERNAL_STORAGE (大部分)像以前一样工作。 “但它确实存在于我存储它的存储中” - 可能已恢复备份。

以上是关于在 Android Q 中从外部存储访问照片的主要内容,如果未能解决你的问题,请参考以下文章

Android外部存储ExternalStorage

Android Q:范围存储中的 SQLite 数据库

在 Android Q 的外部存储中创建 App-Specific 文件夹

在 C++ 中从外部访问嵌套类

如何在 ReactJS 中从“外部”访问组件方法?

在 iOS 13 中从外部存储读取文件