Environment.getExternalStorageDirectory() 在API级别中被废弃 29 java

184 人关注

在android Java上工作,最近更新了SDK到API级别29,现在出现了一个警告,指出

替换代码0】在API级别29中被弃用。

My code is

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

这将是一个什么样的替代方案?

1 个评论
android
deprecated
android-api-levels
Noaman Akram
Noaman Akram
发布于 2019-07-20
13 个回答
CommonsWare
CommonsWare
发布于 2021-07-02
已采纳
0 人赞同

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

或者,修改 mPhotoEditor ,使其能够与 Uri 一起工作,那么。

  • 使用 ACTION_CREATE_DOCUMENT 获得一个 Uri 到用户选择的地点,或

  • 使用 MediaStore ContentResolver insert() 来获取特定类型媒体(如图像)的 Uri --见 这个样本应用程序 这篇文章演示了如何从网站上下载MP4视频。

    另外,请注意,你的 Uri.fromFile ACTION_MEDIA_SCANNER_SCAN_FILE 在安卓7.0以上版本的 FileUriExposedException 上应该是崩溃的。在安卓Q上,只有 MediaStore / insert() 选项会让你的内容迅速被 MediaStore 索引。

    请注意,如果你的 targetSdkVersion 低于30,你可以在安卓10和11上选择退出这些 "范围存储 "的变化,在清单的 <application> 元素中使用 android:requestLegacyExternalStorage="true" 这不是一个长期的解决方案 如果你通过Play Store(也许还有其他地方)发布你的应用程序,你的 targetSdkVersion 将需要在2021年的某个时候达到30或更高。

  • 亲爱的@CommonsWare,如果我们在现实中见面,请提醒我,我欠你一杯啤酒,因为你的博文。我花了整整一个下午的时间来寻找为什么某个外部库在我把targetSdk级别提高到29之后就不再工作了。现在我知道为什么 ...
    使用getExternalFilesDir()和getExternalCacheDir()可以在api29下工作,但是当应用取消安装时,所有的数据都会被使用这个方法删除 ...如果你在应用程序标签中使用这个属性 "android:requestLegacyExternalStorage="true""也可以对api29起作用。当应用程序取消安装时,文件名退出,但数据被删除
    @miladsalimi:对不起,我不明白你的问题。我建议你单独问一个Stack Overflow问题,详细解释你的问题是什么。
    Dims
    There is no getExternalMediaDir in Context , see it yourself: developer.android.com/reference/android/content/Context getExternalMediaDirs 已被废弃,也请看。 developer.android.com/reference/android/content/... 替换代码3】和 getExternalCacheDir() 都会在应用程序卸载时被删除(见同一网址)。 因此,上述任何一项都不能替代 Environment.getExternalStorageDirectory()
    Dims
    这不是关于我的用例,而是上面topicstarter的用例。
    Russell A
    Russell A
    发布于 2021-07-02
    0 人赞同

    用新的API调用获得 destPath

    String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();
        
    我们可以使用这种方法。 developer.android.com/training/data-storage/... ?你怎么看?)
    仍然可以得到环境变量getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)取代Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)。
    更新了被废弃的方法,这个方法修正了外部存储目标29的createNewFile的 "权限拒绝 "错误!谢谢你!
    This is not the public directory
    我试过这段代码,在三星A70安卓10系统中,它对我不起作用。使用这段代码时,目录没有被创建。
    mahmoud mansour
    mahmoud mansour
    发布于 2021-07-02
    0 人赞同

    对于Android Q,你可以在清单中为你的元素添加 android:requestLegacyExternalStorage="true" 。这 opts 你进入了传统的存储模式,你现有的外部存储代码就可以工作。

    <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"来选择退出。

    这个帖子拯救了我在最新版本的Android Studio中用早期开发的代码寻找在外部存储器上存储数据的解决方案--这持续了8小时。谢谢你。
    它将工作到API 29。 "注意。在你更新你的应用程序到目标Android 11(API级别30)之后,当你的应用程序在Android 11设备上运行时,系统会忽略requestLegacyExternalStorage属性,所以你的应用程序必须准备好支持范围存储,并为这些设备上的用户迁移应用程序数据。" developer.android.com/training/data-storage/use-cases
    Not recommended
    Hardik Hirpara
    Hardik Hirpara
    发布于 2021-07-02
    0 人赞同

    在Android 10中创建文件时,请使用 getExternalFilesDir() getExternalCacheDir() 而不是 Environment.getExternalStorageDirectory()

    See below line:

    val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")
        
    你好,我们如何使用 getExternalFilesDir() 与自定义路径来保存图片,我想用它来防止图片显示在画廊中?在默认情况下,它保存在 Andorid/data/packagename/...
    你将需要把文件保存到app目录,然后使用Media uri插入这个文件
    错误的答案......实际问题是如何获得/storage/emulated/0/MyFolder/,但你的答案是/data/user/0/com.example.package/files/MyFolder。
    我试过这段代码,在三星A70安卓10系统中,它对我不起作用。使用这段代码时,目录没有被创建。
    bitvale
    bitvale
    发布于 2021-07-02
    0 人赞同

    这是一个小例子,如果你想用默认的相机拍摄照片并将其存储在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
    

    不要忘记Pre 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
        
    它也被弃用了
    请将其贴在JAVA上
    打开pdf文件而不是图像文件的等价物应该是什么?
    Quick learner
    Quick learner
    发布于 2021-07-02
    0 人赞同

    This worked for me

    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"
        
    0 人赞同

    为了获得内部存储目录,不需要硬编码。

    权限(适用于所有安卓版本)。不要忘记从用户那里获得权限。

    要求访问所有文件

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

    Legacy permission for Android 10 (add this to AndroidManifest.xml > application tag).

    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();
        
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> You need to justify for the usage of this permission & more probability of your app getting rejected from Google Play.
    Rahul Bibave
    Rahul Bibave
    发布于 2021-07-02
    0 人赞同

    This worked

    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)
        
    usilo
    usilo
    发布于 2021-07-02
    0 人赞同

    You could make use of StorageManager & StorageVolume classes

    StorageVolume.getPrimaryStorageVolume() :这个卷是由Environment#getExternalStorageDirectory()和Context#getExternalFilesDir(String)返回的同一个存储设备。

        public String myGetExternalStorageDir() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
                return getPrimaryStorageVolumeForAndroid11AndAbove();
                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;
        
    嘿,@usilo,首先感谢你的回答,这个代码有一个问题,当我使用 getPrimaryStorageVolumeBeforeAndroid11 方法的代码时,它不支持所有的低版本,因为 myStorageManager.getPrimaryStorageVolume() 只能从android N访问。在您的回答中,仍然有比低N版本更低的问号。谢谢
    mad_lad
    mad_lad
    发布于 2021-07-02
    0 人赞同
    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
        
    Piyush
    Piyush
    发布于 2021-07-02
    0 人赞同

    最近我也遇到了类似的问题,由于我的代码太大,我不想在现有的代码中增加新的功能,所以我只是简单地改变了路径。

    Environment.getExternalStorageDirectory() 替换为

    context.getExternalFilesDir(null).getAbsolutePath()。

    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");
        
    084_SAKSHAM GUPTA
    084_SAKSHAM GUPTA
    发布于 2021-07-02
    0 人赞同
      var tempDir: File = inContext.getExternalFilesDir("/")!!
            tempDir = File(tempDir.getAbsolutePath().toString() + "/.IncidentImages/")
            tempDir.mkdir()