programing

Environment.getExternalStorageDirectory()가 API 수준 29 java에서 더 이상 사용되지 않습니다.

css3 2023. 8. 13. 09:54

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_DOCUMENTa를 얻기 위해Uri사용자가 선택한 위치로 이동합니다.

  • 사용하다MediaStore,ContentResolver,그리고.insert()a를 얻기 위해Uri특정 유형의 미디어(예: 이미지) - 웹 사이트에서 MP4 비디오를 다운로드하기 위해 이 작업을 수행하는 것을 보여주는 이 샘플 앱을 참조하십시오.

또한, 주의할 점에 주의하세요.Uri.fromFile와 함께ACTION_MEDIA_SCANNER_SCAN_FILEAndroid 7.0+에서 충돌해야 합니다.FileUriExposedExceptionAndroid Q에서는 오직MediaStore/insert()옵션은 다음을 통해 콘텐츠를 인덱싱합니다.MediaStore얼른.

Android 10 및 11에서 이러한 "범위 스토리지" 변경을 선택할 수 있습니다.targetSdkVersion30 미만, 사용android:requestLegacyExternalStorage="true"에서<application>매니페스트 요소입니다.이것은 장기적인 해결책이 아닙니다.targetSdkVersionPlay 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>

기술적으로, 당신은 당신이 업데이트한 후에만 이것이 필요합니다.targetSdkVersion29까지. 하위 앱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