Environment.getExternalStorageDirectory () tidak digunakan lagi di API level 29 java


109

Bekerja pada android Java, SDK baru-baru ini diupdate ke API level 29, sekarang ada peringatan yang menunjukkan bahwa

Environment.getExternalStorageDirectory() tidak lagi digunakan di API level 29

Kode saya adalah

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

Apa alternatif untuk ini?

Jawaban:


108

Gunakan getExternalFilesDir(),, getExternalCacheDir()atau getExternalMediaDirs()(metode aktif Context) sebagai ganti Environment.getExternalStorageDirectory().

Atau, ubah mPhotoEditoragar dapat bekerja dengan Uri, lalu:

  • Gunakan ACTION_CREATE_DOCUMENTuntuk mendapatkan Urike lokasi yang dipilih pengguna, atau

  • Gunakan MediaStore,, ContentResolverdan insert()untuk mendapatkan Urijenis media tertentu (mis., Gambar) - lihat aplikasi contoh ini yang mendemonstrasikan melakukan ini untuk mengunduh video MP4 dari situs Web

Juga, perhatikan bahwa Uri.fromFiledengan Anda ACTION_MEDIA_SCANNER_SCAN_FILEseharusnya mogok di Android 7.0+ dengan file FileUriExposedException. Di Android Q, hanya opsi MediaStore/ yang insert()akan membuat konten Anda diindeks dengan MediaStorecepat.

Perhatikan bahwa Anda dapat menyisih dari perubahan "penyimpanan terbatas" ini di Android 10 dan 11, jika Anda di targetSdkVersionbawah 30, menggunakan android:requestLegacyExternalStorage="true"dalam <application>elemen manifes. Ini bukan solusi jangka panjang , karena Anda targetSdkVersionharus berusia 30 tahun atau lebih pada 2021 jika Anda mendistribusikan aplikasi melalui Play Store (dan mungkin di tempat lain).


12
Dear @CommonsWare, seandainya kita bertemu secara langsung, tolong ingatkan saya bahwa saya berhutang bir untuk posting blog Anda. Saya menghabiskan sepanjang sore untuk mencari tahu mengapa perpustakaan eksternal tertentu berhenti berfungsi, segera setelah saya menaikkan level targetSdk ke 29. Sekarang saya tahu mengapa ...
Nantoka

1
menggunakan getExternalFilesDir () dan getExternalCacheDir () dapat bekerja dengan api 29 tetapi ketika aplikasi dicopot semua data akan dihapus menggunakan metode ini ... Jika Anda menggunakan atribut ini di dalam tag aplikasi "android: requestLegacyExternalStorage =" true "" dapat bekerja juga untuk api 29. Ketika nama file aplikasi yang tidak dicantumkan keluar tetapi datanya dihapus
Ucdemir

1
@miladsalimi: Maaf, tapi saya tidak mengerti pertanyaan Anda. Saya menyarankan agar Anda mengajukan pertanyaan Stack Overflow terpisah di mana Anda menjelaskan secara rinci apa yang menjadi perhatian Anda.
CommonsWare

3
Tidak ada getExternalMediaDirmasuk Context, lihat sendiri: developer.android.com/reference/android/content/Context Dan getExternalMediaDirssudah usang, lihat juga: developer.android.com/reference/android/content/… Keduanya getExternalFilesDir()dan getExternalCacheDir()dihapus setelah aplikasi di-uninstal ( lihat URL yang sama). Jadi, tidak ada hal di atas yang bisa menggantikan Environment.getExternalStorageDirectory().
Redup

1
Ini bukan tentang kasus penggunaan saya, tapi kasus penggunaan topicstarter di atas.
Redup

33

Dapatkan destPathdengan panggilan API baru:

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

3
Kita dapat menggunakan metode ini: developer.android.com/training/data-storage/… ? Apa yang Anda pikirkan ? :)
aeroxr1

2
Masih bisa mendapatkan variabel lingkungan getExternalFilesDir (Environment.DIRECTORY_DOWNLOADS) menggantikan Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)
Rowan Berry

Memperbarui metode yang tidak digunakan lagi menjadi kesalahan TETAP "Izin ditolak" dari createNewFile untuk penargetan penyimpanan eksternal 29! Terima kasih!
coolcool1994

Ini bukan direktori umum
Irfan Ul Haq

25

Untuk Android Q, Anda dapat menambahkan android:requestLegacyExternalStorage="true"elemen Anda di manifes. Ini memilih Anda untuk menggunakan model penyimpanan lama, dan kode penyimpanan eksternal Anda yang ada akan berfungsi.

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

Secara teknis, Anda hanya memerlukan ini setelah memperbarui targetSdkVersionke 29. Aplikasi dengan targetSdkVersionnilai yang lebih rendah secara default memilih ke penyimpanan lama dan harus android:requestLegacyExternalStorage="false"menyisih.


1
Posting ini telah menyimpan pencarian saya untuk solusi menyimpan data di penyimpanan eksternal di versi terbaru Android Studio dengan kode yang dikembangkan sebelumnya - yang bertahan 8 jam. Terima kasih.
Sriram Nadiminti

1
Ini akan berfungsi hingga API 29. "Perhatian: Setelah Anda mengupdate aplikasi untuk menargetkan Android 11 (API level 30), sistem mengabaikan atribut requestLegacyExternalStorage saat aplikasi Anda berjalan di perangkat Android 11, jadi aplikasi Anda harus siap untuk mendukung cakupan penyimpanan dan untuk memigrasi data aplikasi untuk pengguna di perangkat tersebut. " developer.android.com/training/data-storage/use-cases
avisper

Masih mendapatkan kesalahan yang sama
olajide

6

Silakan gunakan getExternalFilesDir(), getExternalCacheDir()bukan Environment.getExternalStorageDirectory()saat Anda membuat file di Android 10.

Lihat baris di bawah ini:

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

1
Hai, bagaimana kita bisa menggunakan getExternalFilesDir()jalur kustom untuk menyimpan gambar, saya ingin menggunakan ini untuk mencegah menampilkan gambar ke Galeri? Secara default, ini simpan keAndorid/data/packagename/...
milad salimi

Anda perlu menyimpan file ke direktori aplikasi dan kemudian memasukkannya menggunakan Media uri
Ajith Memana

6
jawaban salah .. pertanyaan sebenarnya bagaimana cara mendapatkan / penyimpanan / ditiru / 0 / Folder Saya / tetapi anser /data/user/0/com.example.package/files/MyFolder Anda
Chakder Tarif

5

Ini adalah contoh kecil bagaimana mendapatkan URI untuk file jika Anda ingin mengambil foto menggunakan kamera default dan menyimpannya di folder DCIM (DCIM / app_name / namafile.jpg):

Buka kamera (ingat tentang izin 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)
        }
    }
}

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

Jangan lupa tentang izin WRITE_EXTERNAL_STORAGE untuk pra Q dan tambahkan FileProvider ke AndroidManifest.xml.

Dan dapatkan hasil:

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

itu juga tidak berlaku lagi
Leonardo Henao

1
posting itu java tolong
Attaullah

5

Ini berhasil untuk saya

Tambahkan baris ini di tag aplikasi manifestfile

android:requestLegacyExternalStorage="true"

Contoh

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

Target SDK adalah 29

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

1
Duplikat dari komentar ini: stackoverflow.com/a/61774820/7487335
Josh Correia
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.