英文:
Access photos from external storage in Android Q
问题
最近我将应用的目标版本升级到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); // 自定义具有id和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;
英文:
I recently upgraded the app's target version to API 29. Due to the scoped storage in Android 10, i used MediaStore API to store and retrieve images from app external storage. Earlier, i used getExternalStoragePublicDirectory
to store images taken through camera, now i use MediaStore.Images.Media.EXTERNAL_CONTENT_URI
to write file to an external storage location.
Issue i am facing now is,
When i open my app and take pictures, it stores under a folder name that i gave 'myapp' and i can retrieve my images through Mediastore cursor and show them in a custom gallery. And when i uninstall my app 'myapp' folder still exists. And when i install my app again and try to read the images from the gallery, cursor is not returning any image. But if i take picture again, then i could load them to my custom gallery. Custom gallery view is just a row of images at the bottom of the screen, so that user doesn't have to browse through the photos folder to load the image to the app.
This is how i store my images in the MediaStore
Content values:
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);
Generate Name method:
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;
}
Reading images
{
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();
}
Why the images that i stored in the folder in Mediastore is not being returned by the above code when i reinstall my App. Is it by design or am i missing something?
These are the columns i am retrieving,
final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.MIME_TYPE };
final String orderBy = MediaStore.Images.Media.DATE_TAKEN; ```
</details>
# 答案1
**得分**: 8
因为不清楚的原因,Android 10 认为在不同时间安装的两个应用是不同的应用,与如果这两个应用完全不同一样。
因此,一旦你的应用被卸载,它就失去了对所有自己创建的文件的访问权...即使同一个应用稍后被重新安装。
因此,你需要像处理来自先前安装的应用的文件一样来处理来自完全不相关的应用的文件:请求 `READ_EXTERNAL_STORAGE` 权限来能够查询并读取它们的内容。"读取它们的内容" 部分需要在清单文件的 `<application>` 标签中添加 `android:requestLegacyExternalStorage="true"`。
<details>
<summary>英文:</summary>
For unclear reasons, Android 10 considers two app installations separated by time to be separate apps, no different than if those were two completely different apps.
As a result, once your app is uninstalled, it loses access to all files that it created... even if the same app is later reinstalled.
So, you need to treat files from a previous installation of your app the same way as you would treat files from completely unrelated apps: request `READ_EXTERNAL_STORAGE` to be able to query for them and read their contents. The "read their contents" part requires `android:requestLegacyExternalStorage="true"` on the `<application>` in the manifest.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论