Environment.getExternalStorageDirectory() deprecated in API level 29 java

前端 未结 6 972
梦毁少年i
梦毁少年i 2020-11-22 13:08

Working on android Java, recently updated SDK to API level 29 now there is a warning shown which states that

Environment.getExternalStorageDirectory() i

相关标签:
6条回答
  • 2020-11-22 13:40

    Use getExternalFilesDir(), getExternalCacheDir(), or getExternalMediaDirs() (methods on Context) instead of Environment.getExternalStorageDirectory().

    Or, modify mPhotoEditor to be able to work with a Uri, then:

    • Use ACTION_CREATE_DOCUMENT to get a Uri to a location of the user's choosing, or

    • Use MediaStore, ContentResolver, and insert() to get a Uri for a particular type of media (e.g., an image) — see this sample app that demonstrates doing this for downloading MP4 videos from a Web site

    Also, note that your Uri.fromFile with ACTION_MEDIA_SCANNER_SCAN_FILE should be crashing on Android 7.0+ with a FileUriExposedException. On Android Q, only the MediaStore/insert() option will get your content indexed by the MediaStore quickly.

    Note that you can opt out of these "scoped storage" changes on Android 10 and 11, if your targetSdkVersion is below 30, using android:requestLegacyExternalStorage="true" in the <application> element of the manifest. This is not a long-term solution, as your targetSdkVersion will need to be 30 or higher sometime in 2021 if you are distributing your app through the Play Store (and perhaps elsewhere).

    0 讨论(0)
  • 2020-11-22 13:43

    Get the destPath with the new API call:

    String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();
    
    0 讨论(0)
  • 2020-11-22 13:46

    For Android Q, you can add android:requestLegacyExternalStorage="true" to your element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work.

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

    Technically, you only need this once you update your targetSdkVersion to 29. Apps with lower targetSdkVersion values default to opting into legacy storage and would need android:requestLegacyExternalStorage="false" to opt out.

    0 讨论(0)
  • 2020-11-22 13:49

    This is a small example how to get URI for a file if you want to take photo using default camera and store it in a DCIM folder (DCIM/app_name/filename.jpg):

    Open camera (remember about CAMERA permission):

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

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

    Don't forget about WRITE_EXTERNAL_STORAGE permission for pre Q and add a FileProvider to AndroidManifest.xml.

    And get a result:

    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
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:58

    Please use getExternalFilesDir(), getExternalCacheDir() instead of Environment.getExternalStorageDirectory() when you creating file in Android 10.

    See below line:

    val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")
    
    0 讨论(0)
  • 2020-11-22 13:59

    This worked for me

    Add this line in application tag of manifest file

    android:requestLegacyExternalStorage="true"
    

    Example

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

      defaultConfig {
            minSdkVersion 16
            targetSdkVersion 29
            multiDexEnabled true
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
    0 讨论(0)
提交回复
热议问题