Environment.getExternalStorageDirectory()가 API 수준 29 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");
}
});
}
이에 대한 대안은 무엇입니까?
사용하다getExternalFilesDir()
,getExternalCacheDir()
또는getExternalMediaDirs()
(에 따라 다름)Context
대신에Environment.getExternalStorageDirectory()
.
또는 수정mPhotoEditor
함께 일할 수 있는Uri
그러면:
사용하다
ACTION_CREATE_DOCUMENT
a를 얻기 위해Uri
사용자가 선택한 위치로 이동합니다.사용하다
MediaStore
,ContentResolver
,그리고.insert()
a를 얻기 위해Uri
특정 유형의 미디어(예: 이미지) - 웹 사이트에서 MP4 비디오를 다운로드하기 위해 이 작업을 수행하는 것을 보여주는 이 샘플 앱을 참조하십시오.
또한, 주의할 점에 주의하세요.Uri.fromFile
와 함께ACTION_MEDIA_SCANNER_SCAN_FILE
Android 7.0+에서 충돌해야 합니다.FileUriExposedException
Android Q에서는 오직MediaStore
/insert()
옵션은 다음을 통해 콘텐츠를 인덱싱합니다.MediaStore
얼른.
Android 10 및 11에서 이러한 "범위 스토리지" 변경을 선택할 수 있습니다.targetSdkVersion
30 미만, 사용android:requestLegacyExternalStorage="true"
에서<application>
매니페스트 요소입니다.이것은 장기적인 해결책이 아닙니다.targetSdkVersion
Play Store(및 아마도 다른 곳)를 통해 앱을 배포하는 경우 2021년 중에 30 이상이어야 합니다.
Get thedestPath
새로운 API 호출과 함께:
String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();
Android Q의 경우 추가할 수 있습니다.android:requestLegacyExternalStorage="true"
목록에 있는 당신의 요소로.그러면 기존 스토리지 모델이 선택되고 기존 외부 스토리지 코드가 작동합니다.
<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"
선택을 취소합니다.
이용해주세요getExternalFilesDir()
,getExternalCacheDir()
대신에Environment.getExternalStorageDirectory()
Android 10에서 파일을 만들 때.
아래 줄 참조:
val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")
다음은 기본 카메라를 사용하여 사진을 찍고 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
)
}
preQ에 대한 WRITE_EXTERNAL_STORAGE 권한을 잊지 말고 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
}
}
}
}
}
이것은 나에게 효과가 있었습니다.
응용 프로그램 태그에 이 줄 추가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"
}
하드 코딩 없이 내부 저장소 디렉터리를 가져오려면,
사용 권한(모든 Android 버전에 해당).사용자로부터 권한을 받는 것을 잊지 마십시오.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
Android 10에 대한 기존 권한(AndroidManifest.xml > 응용 프로그램 태그에 추가)
android:requestLegacyExternalStorage="true"
내부 저장소 디렉터리 경로 가져오기:
public static String getInternalStorageDirectoryPath(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
return storageManager.getPrimaryStorageVolume().getDirectory().getAbsolutePath();
} else {
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
}
효과가 있었습니다.
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)
}
}
최근에는 코드가 커서 기존 코드에 새로운 기능을 추가하고 싶지 않기 때문에 경로만 변경합니다.
Environment.getExternalStorageDirectory() 대체 항목
context.get외부 파일 Dir(null)입니다.get AbsolutePath()
코드
private void saveImage() {
if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
final String folderPath = this.getExternalFilesDir(null).getAbsolutePath() + "/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");
}
});
}
다음을 활용할 수 있습니다.StorageManager
&StorageVolume
»
StorageVolume.get Primary 스토리지Volume():이 볼륨은 Environment#get에서 반환한 것과 동일한 스토리지 디바이스입니다.외부 스토리지 디렉토리() 및 컨텍스트 #get외부 파일Dir(문자열)입니다.
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;
}
private fun saveImage(bitmap: Bitmap, name: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver = contentResolver
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + "YOUR_FOLDER")
val imageUri =
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
val fos = resolver.openOutputStream(imageUri!!)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos!!.flush()
fos.close()
toast("Saved to gallery")
} else {
if (isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
val imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM
).toString() + File.separator + "YOUR_FOLDER"
if (!file.exists()) {
file.mkdir()
}
val image = File(imagesDir, "$name.png")
val fos = FileOutputStream(image)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos.flush()
fos.close()
} else {
// ask for permission
}
}
}
var tempDir: File = inContext.getExternalFilesDir("/")!!
tempDir = File(tempDir.getAbsolutePath().toString() + "/.IncidentImages/")
tempDir.mkdir()
만약 당신이 XARAMIN과 함께 일하고 (나처럼) 이 모든 다른 대답에 혼란스럽다면, 다음의 예를 따라라:
var picture = new Java.IO.File(Environment.DirectoryPictures, "fileName");
이걸 알아내는 데 시간이 좀 걸렸습니다.
언급URL : https://stackoverflow.com/questions/57116335/environment-getexternalstoragedirectory-deprecated-in-api-level-29-java
'programing' 카테고리의 다른 글
내 날짜 시간을 UTC로 변환하는 중 문제 발생 (0) | 2023.08.13 |
---|---|
C에서 "개인/제한된" 기능을 구현하는 방법은 무엇입니까? (0) | 2023.08.13 |
부트스트랩 버튼을 클릭하면 파란색 윤곽선이 표시됨 (0) | 2023.08.13 |
판다 데이터 프레임에 하나 이상의 NaN 값이 있는 행 표시 (0) | 2023.08.13 |
UI 텍스트 보기의 줄 수 제한 (0) | 2023.08.13 |