Android API级别29 java中不推荐使用Environment.getExternalStorageDirectory()
在android Java上工作,最近将SDK更新为API级别29,现在显示了一条警告,指出Android API级别29 java中不推荐使用Environment.getExternalStorageDirectory(),android,deprecated,android-api-levels,Android,Deprecated,Android Api Levels,在android Java上工作,最近将SDK更新为API级别29,现在显示了一条警告,指出 Environment.getExternalStorageDirectory()在API级别29中不推荐使用 我的代码是 private void saveImage() { if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { final String folderPath = Environment.g
Environment.getExternalStorageDirectory()
在API级别29中不推荐使用
我的代码是
private void saveImage() {
if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
final String folderPath = Environment.getExternalStorageDirectory() + "/PhotoEditors";
File folder = new File(folderPath);
if (!folder.exists()) {
File wallpaperDirectory = new File(folderPath);
wallpaperDirectory.mkdirs();
}
showLoading("Saving...");
final String filepath=folderPath
+ File.separator + ""
+ System.currentTimeMillis() + ".png";
File file = new File(filepath);
try {
file.createNewFile();
SaveSettings saveSettings = new SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build();
if(isStoragePermissionGranted() ) {
mPhotoEditor.saveAsFile(file.getAbsolutePath(), saveSettings, new PhotoEditor.OnSaveListener() {
@Override
public void onSuccess(@NonNull String imagePath) {
hideLoading();
showSnackbar("Image Saved Successfully");
mPhotoEditorView.getSource().setImageURI(Uri.fromFile(new File(imagePath)));
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(new File(filepath))));
Intent intent = new Intent(EditImageActivity.this, StartActivity.class);
startActivity(intent);
finish();
}
@Override
public void onFailure(@NonNull Exception exception) {
hideLoading();
showSnackbar("Failed to save Image");
}
});
}
有什么替代方法?使用
getExternalFilesDir()
,getExternalCacheDir()
,或getExternalMediaDirs()
(Context
上的方法)而不是环境。getExternalStorageDirectory()
或者,修改mPhotoEditor
以能够使用Uri
,然后:
- 使用
将ACTION\u CREATE\u DOCUMENT
获取到用户选择的位置,或Uri
- 使用
、MediaStore
和ContentResolver
获取特定类型媒体(例如图像)的insert()
——请参见演示如何从网站下载MP4视频Uri
Uri.fromFile
和ACTION\u MEDIA\u SCANNER\u SCAN\u FILE
应该会在Android 7.0+上崩溃,出现FileUriExposedException
。在安卓Q上,只有MediaStore
/insert()
选项才能通过MediaStore
快速索引您的内容
请注意,如果您的
targetSdkVersion
低于30,您可以使用清单的元素中的Android:requestLegacyExternalStorage=“true”
退出Android 10和11上的这些“范围存储”更改。这不是一个长期的解决方案,因为如果您通过Play Store(或者其他地方)分发应用程序,您的targetSdkVersion
将需要在2021年的某个时候达到30或更高版本。使用新的API调用获取destPath
:
String destPath=mContext.getExternalFilesDir(null.getAbsolutePath();
这是一个小示例,如果您想使用默认相机拍照并将其存储在DCIM文件夹(DCIM/app\u name/filename.jpg)中,如何获取文件的URI:
打开相机(记住相机权限):
并获取URI:
private fun getPhotoFileUri(): Uri {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val fileName = "IMG_${timeStamp}.jpg"
var uri: Uri? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver = requireContext().contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/app_name/")
}
uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
}
return uri ?: getUriForPreQ(fileName)
}
private fun getUriForPreQ(fileName: String): Uri {
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
val photoFile = File(dir, "/app_name/$fileName")
if (photoFile.parentFile?.exists() == false) photoFile.parentFile?.mkdir()
return FileProvider.getUriForFile(
requireContext(),
"ru.app_name.fileprovider",
photoFile
)
}
不要忘记为pre Q写入外部存储权限,并向AndroidManifest.xml添加文件提供程序
并得到一个结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_IMAGE_CAPTURE -> {
photoURI?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val thumbnail: Bitmap =
requireContext().contentResolver.loadThumbnail(
it, Size(640, 480), null
)
} else {
// pre Q actions
}
}
}
}
}
在Android 10中创建文件时,请使用getExternalFilesDir()
,getExternalCacheDir()
而不是Environment.getExternalStorageDirectory()
见下一行:
val file=file(this.externalCacheDir!!.absolutePath,“/your\u file\u name”)
对于Android Q,您可以将Android:requestLegacyExternalStorage=“true”
添加到清单中的元素中。这将把您引入到遗留存储模型中,并且您现有的外部存储代码将正常工作
...
从技术上讲,只有在将targetSdkVersion
更新为29之后,您才需要此选项。具有较低targetSdkVersion
值的应用程序默认选择传统存储,并且需要android:requestLegacyExternalStorage=“false”
才能选择退出。这对我很有效
在清单
文件的应用程序标记中添加此行
android:requestLegacyExternalStorage="true"
示例
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
</application>
这起作用了
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentResolver?.also { resolver ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "Image_"+".jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator+ "TestFolder")
}
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos)
Objects.requireNonNull(fos)
}
}
您可以使用StorageManager
和StorageVolume
类
:此卷是由环境#getExternalStorageDirectory()和上下文#getExternalFilesDir(字符串)返回的同一存储设备
公共字符串myGetExternalStorageDir(){
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.R)
返回GetPrimaryStorageVolumeForandRoid11及以上();
其他的
返回getPrimaryStorageVolumeBeforeAndroid11();
}
@TargetApi(Build.VERSION\u code.R)
私有字符串GetPrimaryStorageVolumeForandRoid11及以上(){
StorageManager myStorageManager=(StorageManager)ctx.getSystemService(Context.STORAGE\u服务);
StorageVolume mySV=myStorageManager.getPrimaryStorageVolume();
返回mySV.getDirectory().getPath();
}
私有字符串getPrimaryStorageVolumeBeforeAndroid11(){
字符串volumeRootPath=“”;
StorageManager myStorageManager=(StorageManager)ctx.getSystemService(Context.STORAGE\u服务);
StorageVolume mySV=myStorageManager.getPrimaryStorageVolume();
类storageVolumeClazz=null;
试一试{
storageVolumeClazz=Class.forName(“android.os.storage.StorageVolume”);
方法getPath=storageVolumeClazz.getMethod(“getPath”);
volumeRootPath=(字符串)getPath.invoke(mySV);
}捕获(例外e){
e、 printStackTrace();
}
返回卷路径;
}
亲爱的@commonware,如果我们真的在现场见面,请提醒我,我欠你一杯啤酒,谢谢你的博客帖子。当我将targetSdk级别提升到29级时,我花了整整一个下午的时间找出某个外部库停止工作的原因。现在我知道了为什么…使用getExternalFilesDir()和getExternalCacheDir()可以与api 29一起工作,但是当应用程序未安装时,所有数据都将使用此方法删除。。。如果在应用程序标记“android:requestLegacyExternalStorage=“true”中使用此属性,则api 29也可以使用此属性。当应用程序未安装的文件名退出但数据removed@miladsalimi:对不起,我不明白你的问题。我建议你问一个单独的堆栈溢出问题,详细解释你所关心的是什么。在上下文中没有getExternalMediaDir
,你自己看看吧:getExternalMediaDir
被弃用了,再看看:getExternalFilesDir()
和getExternalCacheDir()
在卸载应用程序时被删除(请参见相同的URL)。因此,上面的任何一个都不能替代环境。getExternalStorageDirectory()
。这不是我的用例,而是topicstarter
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentResolver?.also { resolver ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "Image_"+".jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator+ "TestFolder")
}
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos)
Objects.requireNonNull(fos)
}
}