Environment.getExternalStorageDirectory () API seviyesi 29 java'da kullanımdan kaldırıldı


102

Android Java üzerinde çalışan, yakın zamanda SDK'yı API seviyesi 29'a güncelledi, şimdi gösterilen bir uyarı var

Environment.getExternalStorageDirectory() API seviyesi 29'da kullanımdan kaldırıldı

Benim kodum

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");
            }
       });
   }

Bunun alternatifi ne olacak?

Yanıtlar:


94

Kullanım getExternalFilesDir(), getExternalCacheDir()ya getExternalMediaDirs()(yöntemleri üzerine Contextyerine) Environment.getExternalStorageDirectory().

Veya mPhotoEditora ile çalışabilmek için değiştirin Uri, ardından:

  • Kullanıcının seçtiği bir konuma ACTION_CREATE_DOCUMENTulaşmak için kullanın Uriveya

  • Kullanım MediaStore, ContentResolverve insert()bir olsun Urimedyaya özel bir tip (örneğin, bir resim) için - bkz bu örnek uygulamayı bir Web sitesinden MP4 video indirme için yapıyor koyduğunu

Ayrıca, not olduğunu senin Uri.fromFileile ACTION_MEDIA_SCANNER_SCAN_FILEbir ile 7.0+ Android'de kilitleniyor edilmelidir FileUriExposedException. Android Q'da yalnızca MediaStore/ insert()seçeneği, içeriğinizin MediaStorehızlı bir şekilde dizine eklenmesini sağlar.

Senin eğer, Android 10 ve 11. Bu "kapsamlı depolama" değişikliklerin kapsamı dışında kalmak anlamına Not targetSdkVersionkullanarak, 30 altındadır android:requestLegacyExternalStorage="true"içinde <application>tezahür unsuru. Bu, uzun vadeli bir çözüm değildir , çünkü targetSdkVersionuygulamanızı Play Store üzerinden (ve belki de başka bir yerde) dağıtıyorsanız 2021'de bir ara 30 veya daha yüksek olması gerekecek.


11
Sevgili @CommonsWare, gerçek hayatta tanışırsak, lütfen blog gönderiniz için size bir bira borçlu olduğumu hatırlatın. TargetSdk düzeyini 29'a yükselttiğimde, belirli bir harici kütüphanenin neden çalışmayı bıraktığını öğrenmek için bütün bir öğleden sonra geçirdim. Şimdi nedenini biliyorum ...
Nantoka

1
getExternalFilesDir () ve getExternalCacheDir () kullanmak api 29 ile çalışabilir, ancak uygulama çağrılmadığında tüm veriler bu yöntemler kullanılarak kaldırılacaktır ... Bu özniteliği "android: requestLegacyExternalStorage =" true "" uygulama etiketi içinde kullanırsanız, api için de çalışabilir 29. Uygulamanın unistalled dosya adı çıktığı ancak veriler
silindiğinde

1
@miladsalimi: Üzgünüm ama sorunuzu anlamadım. Endişenizin ne olduğunu ayrıntılı olarak açıklayacağınız ayrı bir Stack Overflow sorusu sormanızı öneririm.
CommonsWare

3
Orada hayır getExternalMediaDiriçinde Contextkendiniz bakın: developer.android.com/reference/android/content/Context Ve getExternalMediaDirs: itiraz edildi, ayrıca bakmak developer.android.com/reference/android/content/... Hem getExternalFilesDir()ve getExternalCacheDir()(uygulama kaldırma üzerine silinir aynı URL'ye bakın). Yani yukarıdakilerin hiçbiri ikame edemez Environment.getExternalStorageDirectory().
ölçülü yük

1
Bu benim kullanım durumumla ilgili değil, yukarıdaki konu başlatıcının kullanım durumuyla ilgili.
ölçülü yük

29

destPathYeni API çağrısıyla alın :

String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();

3
Bu yöntemi kullanabiliriz: developer.android.com/training/data-storage/… ? Bu konu hakkında ne düşünüyorsun ? :)
aeroxr1

2
Hala ortam değişkeni getExternalFilesDir (Environment.DIRECTORY_DOWNLOADS) alabilir miyim Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) yerini
Rowan Berry

Kullanımdan kaldırılan yöntem, harici depolamayı hedefleyen 29 için createNewFile'daki bu SABİT "İzin reddedildi" hatasıyla güncellendi! Teşekkür ederim!
coolcool1994

Bu genel dizin değil
Irfan Ul Haq

22

Android Q için, android:requestLegacyExternalStorage="true"bildirimdeki öğenize ekleyebilirsiniz . Bu seçmesi eski depolama modeline içine ve mevcut harici depolama kod çalışacaktır.

<manifest ... >
<!-- This attribute is "false" by default on apps targeting
     Android 10 or higher. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Teknik olarak, buna yalnızca targetSdkVersion29'unuzu güncellediğinizde ihtiyacınız olacaktır. Daha düşük targetSdkVersiondeğerlere sahip uygulamalar, varsayılan olarak eski depolamayı seçer ve android:requestLegacyExternalStorage="false"devre dışı bırakmaları gerekir .


1
Bu gönderi, daha önce geliştirilmiş bir kodla (8 saat süren) Android Studio'nun en son sürümünde harici depolama alanında veri depolamak için bir çözüm arayışımı kaydetti. Teşekkür ederim.
Sriram Nadiminti

1
API 29'a kadar çalışacaktır. "Dikkat: Uygulamanızı Android 11'i (API seviyesi 30) hedefleyecek şekilde güncelledikten sonra, sistem, uygulamanız Android 11 cihazlarda çalışırken requestLegacyExternalStorage özniteliğini yoksayar, bu nedenle uygulamanız kapsamlı desteklemeye hazır olmalıdır depolama ve bu cihazlardaki kullanıcılar için uygulama verilerini taşımak. " developer.android.com/training/data-storage/use-cases
avisper

5

Bu, varsayılan kamerayı kullanarak fotoğraf çekmek ve bir DCIM klasöründe (DCIM / uygulama_adı / dosyaadı.jpg) saklamak istiyorsanız bir dosya için URI'nin nasıl alınacağına dair küçük bir örnektir:

Kamerayı açın (KAMERA iznini unutmayın):

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)
        }
    }
}

Ve URI'yi alın:

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 öncesi için WRITE_EXTERNAL_STORAGE iznini unutmayın ve AndroidManifest.xml'e bir FileProvider ekleyin.

Ve bir sonuç alın:

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
                }
            }
        }
    }
}

ayrıca kullanımdan kaldırıldı
Leonardo Henao

java
postala

5

Kullanın getExternalFilesDir(), getExternalCacheDir()yerine Environment.getExternalStorageDirectory()Android 10'da dosyayı oluştururken.

Aşağıdaki satıra bakın:

val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")

1
Merhaba, getExternalFilesDir()resim kaydetmek için özel yolla nasıl kullanabiliriz, bunu resimlerin Galeri'ye gösterilmesini önlemek için kullanmak istiyorum? Varsayılan olarak kaydedinAndorid/data/packagename/...
milad salimi

Dosyayı uygulama dizinine kaydetmeniz ve ardından bunu Media
uri'yi

3
yanlış cevap .. gerçek soru nasıl alınır / depolanır / taklit edilir / 0 / MyFolder / ancak yanıt vericiniz /data/user/0/com.example.package/files/MyFolder
Tarif Chakder

3

Bu benim için çalıştı

Bu satırı manifestdosyanın uygulama etiketine ekleyin

android:requestLegacyExternalStorage="true"

Misal

 <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>

Hedef SDK 29

  defaultConfig {
        minSdkVersion 16
        targetSdkVersion 29
        multiDexEnabled true
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

1
Bu yorumun
kopyası
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.