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");
}
});
}
이것에 대한 대안은 무엇입니까?
답변
사용 getExternalFilesDir()
, getExternalCacheDir()
또는 getExternalMediaDirs()
(방법에 대한 Context
대신) Environment.getExternalStorageDirectory()
.
또는에서 mPhotoEditor
작업 할 수 있도록 수정 Uri
한 다음 :
-
사용자가 선택한 위치
ACTION_CREATE_DOCUMENT
로 이동하는Uri
데 사용 합니다. -
사용
MediaStore
,ContentResolver
및insert()
를 얻기 위해Uri
미디어의 특정 유형 (예를 들어, 이미지)에 대한 – 볼 이 샘플 응용 프로그램 웹 사이트에서 MP4 비디오를 다운로드하는이 일을 보여줍니다
또한, 참고 귀하 Uri.fromFile
와 ACTION_MEDIA_SCANNER_SCAN_FILE
로모그래퍼 7.0 이상 안드로이드에 충돌한다 FileUriExposedException
. Android Q에서는 MediaStore
/ insert()
옵션 만MediaStore
빠르게 .
Android 10 및 11에서 targetSdkVersion
30 미만인 경우 매니페스트 android:requestLegacyExternalStorage="true"
의 <application>
요소를 사용하여 이러한 ‘범위가 지정된 저장소’변경 사항을 선택 해제 할 수 있습니다 . 이는 장기적인 해결책이 아니다 당신으로, targetSdkVersion
당신이 (다른 곳에서 아마와) Play 스토어를 통해 응용 프로그램을 배포하는 경우 2021 년 30 이상 언젠가 될 의지가 필요합니다.
답변
가져 오기 destPath
새로운 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()
당신은 안드로이드 (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
)
}
사전 Q에 대한 WRITE_EXTERNAL_STORAGE 권한을 잊지 말고 AndroidManifest.xml에 FileProvider를 추가하십시오.
결과를 얻으십시오.
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"
}