在Android Pie之前,我总是将应用程序需要存储的文件存储在< code>/sdcard/my-app/上,这是通过
文件fBaseDir=新文件(Environment.getExextalStorageDirectory(),"my-app");
我的一个应用程序在< code >/SD card/my-App/favicons 中存储了数百个(多达数千个)favicon,当用户创建应用程序的备份时,它会存储在< code >/SD card/my-App/backups 中。Google Chrome在与我的应用程序共享时拍摄的网页截图存储在< code >/SD card/my-app/screens 中,等等。
当我卸载该应用程序时,我可以稍后重新安装它,并且仍然拥有所有网站图标,屏幕截图和备份可用于恢复状态,而不必重新下载所有收藏夹图标(并且可能丢失了所有其他数据)。
我还可以通过将/sdcard/my-app/
目录复制到我的PC来轻松创建备份,并在另一部手机上恢复它,例如当我迁移到新设备时。
我还可以使用文件资源管理器查看该目录中的哪些文件,然后选择并通过电子邮件发送它们或将它们复制到Google Drive,或者专门删除其中的一些(例如旧的备份文件)。
在Android 10中,这种方法已经崩溃。我无法将收藏夹图标添加到Images Media Folder,因为它们不是真实的图像,它们会不必要地扰乱视图,并可能因此而禁止我的开发者帐户进入Play Store。我也不想将所有这些数据存储在App Private目录中,因为当应用被卸载时,这些数据会被删除,而且其他应用(如浏览器)无法访问这些数据。
我有哪些选择?手动选择文件或目录不是一个选项,除非只需要选择一次目录,然后永久授予访问权限(读取和写入文件)。
我从不在/sdcard/my-app/
之外阅读或写作。
在Android Q中,默认情况下,对其私人文件夹之外的应用程序禁用直接文件访问。以下是您可以在您的案例中使用的几个策略:
requestLegacyExternalStorage
来具有旧行为,但它将不再适用于Android R,因此它实际上是一个短期解决方案;getExternalFilesDir()
方法保存文件。这是您的私人文件夹,其他应用程序只有在具有READ_EXTERNAL_STORAGE
权限的情况下才能访问这些文件。在这种情况下,最好使用 FileProvider
授予对您的文件的其他应用程序的访问权限。StorageManager
的 getPrimaryStorageVolume().createOpenDocumentTreeIntent()
方法请求访问外部主卷。在这种情况下,您需要用户同意,并且无论如何都无法直接使用File
api,但是使用DocumentFile
类,您具有非常相似的接口,因此这是更接近旧行为的解决方案。如果您需要在前台和后台执行操作,即没有用户交互,除了第一次交互以请求权限,它就可以工作。我链接第3点的Flipper库,它有助于管理旧Android版本中的文件。
从Android 11开始,如果您需要访问外部文件目录,您需要在清单中添加MANAGE_EXTERNAL_STORAGE
权限。
通过执行以下操作,应用程序可以请求用户访问所有文件:
1.在清单中声明MANAGE_EXTERNAL_STORAGE
权限。
2.使用ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
意图操作将用户引导到系统设置页面,在该页面中他们可以为您的应用启用以下选项:允许访问以管理所有文件。
3.要确定您的应用程序是否已被授予< code > MANAGE _ EXTERNAL _ STORAGE 权限,请调用< code > environment . isexternalstoragemanager()。
在这里读它
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
}
}