正在使用Android Java(最近将SDK更新到API级别29),现在显示警告,指出该状态
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");
}
});
}
有什么替代方法?
答案 0 :(得分:24)
对于Android Q,您可以将android:requestLegacyExternalStorage="true"
添加到清单中的元素。 opts使您进入传统存储模型,并且您现有的外部存储代码将起作用。
<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
从技术上讲,仅在将targetSdkVersion
更新为29时才需要这样做。具有较低targetSdkVersion
值的应用默认会选择使用旧版存储,并且需要android:requestLegacyExternalStorage="false"
退出。
答案 1 :(得分:9)
通过新的API调用获取destPath
:
String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();
答案 2 :(得分:6)
在Android 10中创建文件时,请使用getExternalFilesDir()
,getExternalCacheDir()
而不是Environment.getExternalStorageDirectory()
。
请参见以下行:
val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")
答案 3 :(得分:5)
这对我有用
将此行添加到manifest
文件的应用程序标记中
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>
目标SDK为29
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
答案 4 :(得分:3)
这有效
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)
}
}
答案 5 :(得分:1)
您可以使用 StorageManager
和 StorageVolume
类
StorageVolume.getPrimaryStorageVolume():此卷与 Environment#getExternalStorageDirectory() 和 Context#getExternalFilesDir(String) 返回的存储设备相同。
public String myGetExternalStorageDir() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
return getPrimaryStorageVolumeForAndroid11AndAbove();
else
return getPrimaryStorageVolumeBeforeAndroid11();
}
@TargetApi(Build.VERSION_CODES.R)
private String getPrimaryStorageVolumeForAndroid11AndAbove() {
StorageManager myStorageManager = (StorageManager) ctx.getSystemService(Context.STORAGE_SERVICE);
StorageVolume mySV = myStorageManager.getPrimaryStorageVolume();
return mySV.getDirectory().getPath();
}
private String getPrimaryStorageVolumeBeforeAndroid11() {
String volumeRootPath = "";
StorageManager myStorageManager = (StorageManager) ctx.getSystemService(Context.STORAGE_SERVICE);
StorageVolume mySV = myStorageManager.getPrimaryStorageVolume();
Class<?> storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getPath = storageVolumeClazz.getMethod("getPath");
volumeRootPath = (String) getPath.invoke(mySV);
} catch (Exception e) {
e.printStackTrace();
}
return volumeRootPath;
}
答案 6 :(得分:0)
使用getExternalFilesDir()
,getExternalCacheDir()
或getExternalMediaDir()
(Context
上的方法)代替Environment.getExternalStorageDirectory()
。
或者,将mPhotoEditor
修改为可以与Uri
一起使用,然后:
使用ACTION_CREATE_DOCUMENT
将Uri
移至用户选择的位置,或者
使用MediaStore
,ContentResolver
和insert()
获取特定媒体类型(例如图像)的Uri
-请参阅{{3 }}演示了如何从网站下载MP4视频
此外,请注意,带有Uri.fromFile
的{{1}}在带有ACTION_MEDIA_SCANNER_SCAN_FILE
的Android 7.0及更高版本上应该会崩溃。在Android Q上,只有FileUriExposedException
/ MediaStore
选项将使您的内容迅速被insert()
索引。
有关Android Q如何影响外部存储的更多信息,请参见this sample app。
答案 7 :(得分:0)
这是一个小示例,如果您想使用默认相机拍照并将其存储在DCIM文件夹(DCIM / app_name / filename.jpg)中,如何获取文件的URI:
打开相机(记住有关CAMERA的权限):
private var photoURI: Uri? = null
private fun openCamera() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
photoURI = getPhotoFileUri()
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
takePictureIntent.resolveActivity(requireActivity().packageManager)?.also {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
}
并获取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
)
}
不要忘记对Q的WRITE_EXTERNAL_STORAGE权限,并将FileProvider添加到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
}
}
}
}
}
答案 8 :(得分:0)
// Print SubStrings using recursion
void pss(string s, int start, int end)
{
if(start==s.length()-1)
{
cout<<s.substr(start)<<endl;
return;
}
if(end==s.length()+1)
{
start++;
end=start+1;
pss(s,start,end);
}
else if( start<=s.length()&&end<=s.length())
{
cout<<s.substr(start,end-start)<<endl;
pss(s,start,end+1);
}
}
int main()
{
string s;
cin>>s;
pss(s,0,1);
return 0;
}
答案 9 :(得分:0)
如果您使用 XAMARIN 并且对所有这些不同的答案感到困惑(像我一样),请按照以下示例操作:
var picture = new Java.IO.File(Environment.DirectoryPictures, "fileName");
我花了一些时间才弄明白。