Environment.getExternalStorageDirectory ()는 API 레벨 29 Java에서 더 이상 사용되지 않습니다.

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, ContentResolverinsert()를 얻기 위해 Uri미디어의 특정 유형 (예를 들어, 이미지)에 대한 – 볼 이 샘플 응용 프로그램 웹 사이트에서 MP4 비디오를 다운로드하는이 일을 보여줍니다

또한, 참고 귀하 Uri.fromFileACTION_MEDIA_SCANNER_SCAN_FILE로모그래퍼 7.0 이상 안드로이드에 충돌한다 FileUriExposedException. Android Q에서는 MediaStore/ insert()옵션 만MediaStore 빠르게 .

Android 10 및 11에서 targetSdkVersion30 미만인 경우 매니페스트 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"
    }