Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中
Posted
技术标签:
【中文标题】Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中【英文标题】:Android 10: What are my options to save files on external storage into a directory called "/sdcard/my-app/"Android 10:我有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中 【发布时间】:2020-01-07 09:01:26 【问题描述】:在 android Pie 之前,我总是将应用程序需要存储的文件存储在 /sdcard/my-app/
上,我是通过它获得的
File fBaseDir = new File(Environment.getExternalStorageDirectory(), "my-app");
我的一个应用程序在/sdcard/my-app/favicons
中存储了数百个(多达数千个)图标,当用户创建应用程序的备份时,它会存储在/sdcard/my-app/backups
中。与我的应用共享时由 Google Chrome 拍摄的网页屏幕截图存储在 /sdcard/my-app/screenshots
中,以此类推。
当我卸载应用程序时,我可以稍后重新安装它,并且仍然拥有所有可用于恢复状态的网站图标、屏幕截图和备份,而无需重新下载所有网站图标(并且可能丢失了所有其他数据) .
我还可以轻松地创建/sdcard/my-app/
目录的备份,方法是将其复制到我的电脑上,然后在另一部手机上恢复它,例如当我迁移到新设备时。
我还可以使用文件资源管理器查看该目录中有哪些文件,然后选择并通过电子邮件发送它们或将它们复制到 Google 云端硬盘,或者专门删除其中的一些(例如旧的备份文件)。
在 Android 10 中,这种方法已经失效。我无法将网站图标添加到图像媒体文件夹中,因为它们不是真实图像,它们会不必要地使视图混乱,并且可能会因为这样做而禁止我的开发者帐户进入 Play 商店。我也不想将所有这些数据存储在 App-Private 目录中,因为当应用程序被卸载时它会被删除,而且像资源管理器这样的其他应用程序无法访问它们。
我有哪些选择?手动选择文件或目录不是一种选择,除非只需要选择一次目录,然后永久授予访问权限(读取和写入文件)。
我从不在/sdcard/my-app/
之外读写。
【问题讨论】:
对于初学者来说仍然有android:requestLegacyExternalStorage
模式,虽然这是暂时的,第二种方法是使用SAF,让用户选择根目录并授予您的应用程序访问权限,然后您几乎可以处理复制,移动和其他使用DocumentFile
@DarShan 谢谢,android:requestLegacyExternalStorage
在开发过程中会很有用。是否通过 SAF 永久授权访问?
android:requestLegacyExternalStorage
也将用于生产,直到下一个主要版本。关于 SAF 权限是永久的,您可以保存在onActivityResult()
中获得的Uri
并使用grantPermission()
和getContentResolver().takePersistableUriPermission()
是的,Android 11/R 等等。 :)
您的用例是绝对合法的,令人震惊的是,Google 没有人关心这个“功能”会不可逆转地破坏多少应用程序。
【参考方案1】:
在 Android Q 中,对于私有文件夹之外的应用,默认情况下禁用直接文件访问。您可以在以下情况下使用以下几种策略:
-
使用 manifest 选项
requestLegacyExternalStorage
来获得旧行为,但它不再适用于 Android R,因此它确实是一个短期解决方案;
使用getExternalFilesDir()
方法保存文件。这是您的私人文件夹,其他应用只有拥有READ_EXTERNAL_STORAGE
权限才能访问这些文件。在这种情况下,最好使用 FileProvider
授予对您文件的其他应用程序的访问权限。
使用StorageManager
类的方法getPrimaryStorageVolume().createOpenDocumentTreeIntent()
请求访问外部主卷。在这种情况下,您需要用户同意并且无论如何您将无法直接使用File
api,但是使用DocumentFile
类您有一个非常相似的界面,所以这是更接近旧行为的解决方案。如果您需要在前台和后台执行操作,即无需用户交互,但请求权限的第一次交互除外。
我为第 3 点链接了 Flipper 库,它有助于像在旧版 android 中一样管理文件。
【讨论】:
因此,如果我将文件保存到我的应用程序目录 (context.getExternalFilesDir
),我将能够使用 File mFile = new File(PathToExternalFilesDir);
访问该文件?然后,这将在调用 mFile.getAbsoluteFile()
时返回 file://
Uri - 我的应用程序可以使用/使用该文件 uri 吗?
私有目录中允许文件接口
@HB。没错,自 Android-Q 以来,即使没有任何权限,应用程序也可以访问其特定目录。此外,没有其他应用程序可以访问特定的应用程序沙箱目录。查看作用域存储影响汇总表developer.android.com/training/data-storage/files/…
您的第 2 点很棘手。我尝试使用 DownloadManager 并将目标设置为 getExternalFilesDir()。据说下载完成了,但是一旦我检查文件是否存在,它就不存在了。
对不起兄弟!无法理解如何检查文件是否存在,也无法正确理解如何使用 Flipper 编写代码。所以,我拒绝投票这个答案!因为我真的需要帮助..【参考方案2】:
从 Android 11 开始,如果您需要访问外部文件目录,则需要在 Manifest 中添加 MANAGE_EXTERNAL_STORAGE
权限。
应用可以通过执行以下操作请求用户访问所有文件:
1. 在清单中声明MANAGE_EXTERNAL_STORAGE
权限。
2.使用ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
意图操作将用户引导至系统设置页面,他们可以在其中为您的应用启用以下选项:允许访问以管理所有文件。强>
3.要确定您的应用是否已获得MANAGE_EXTERNAL_STORAGE
权限,请致电Environment.isExternalStorageManager()
。
在这里阅读here
【讨论】:
这并不完全正确。只要您不在 Play Store 上发布您的应用程序,它就可以工作。但是,如果您确实发布了它,您的应用程序必须属于一组特定的用例才能获得许可。 developer.android.com/training/data-storage/…【参考方案3】:对于上面的android Q 这是代码可以正常工作以将图像保存在外部存储上 //不要忘记添加Manifest权限 //
package com.myretro.saveimager;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.OutputStream;
public class MainActivity extends AppCompatActivity
private Button btnsave;
private ImageView myImage;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnsave = findViewById(R.id.button);
myImage = findViewById(R.id.imageView);
btnsave.setOnClickListener(view ->
BitmapDrawable bitmap1 = (BitmapDrawable) myImage.getDrawable();
Bitmap bitmap = bitmap1.getBitmap();
saveImmageIntoExternalStrogaeaboveQ(bitmap);
);
private void saveImmageIntoExternalStrogaeaboveQ(Bitmap bitmap)
OutputStream outputStream;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
ContentResolver contentResolver = getContentResolver();
ContentValues mValue = new ContentValues();
mValue.put(MediaStore.MediaColumns.DISPLAY_NAME, "hacker" + ".jpg");
mValue.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
mValue.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "myimage");
Uri imageuri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mValue);
try
outputStream = contentResolver.openOutputStream(imageuri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
Toast.makeText(this, "Image Saved SuccessFull", Toast.LENGTH_SHORT).show();
catch (FileNotFoundException e)
Toast.makeText(this, "Something went wrong " + e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
else
//old method to write
【讨论】:
以上是关于Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中的主要内容,如果未能解决你的问题,请参考以下文章
使用 Flutter 时我都有哪些存储数据的选项? (iOS和Android)[关闭]