Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中

Posted

技术标签:

【中文标题】Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中【英文标题】:Android 10: What are my options to save files on external storage into a directory called "/sdcard/my-app/"Android 10:我有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中 【发布时间】:2020-01-07 09:01:26 【问题描述】:

android Pie 之前,我总是将应用程序需要存储的文件存储在 /sdcard/my-app/ 上,我是通过它获得的

File fBaseDir = new File(Environment.getExternalStorageDirectory(), "my-app");

我的一个应用程序在/sdcard/my-app/favicons 中存储了数百个(多达数千个)图标,当用户创建应用程序的备份时,它会存储在/sdcard/my-app/backups 中。与我的应用共享时由 Google Chrome 拍摄的网页屏幕截图存储在 /sdcard/my-app/screenshots 中,以此类推。

当我卸载应用程序时,我可以稍后重新安装它,并且仍然拥有所有可用于恢复状态的网站图标、屏幕截图和备份,而无需重新下载所有网站图标(并且可能丢失了所有其他数据) .

我还可以轻松地创建/sdcard/my-app/ 目录的备份,方法是将其复制到我的电脑上,然后在另一部手机上恢复它,例如当我迁移到新设备时。

我还可以使用文件资源管理器查看该目录中有哪些文件,然后选择并通过电子邮件发送它们或将它们复制到 Google 云端硬盘,或者专门删除其中的一些(例如旧的备份文件)。

在 Android 10 中,这种方法已经失效。我无法将网站图标添加到图像媒体文件夹中,因为它们不是真实图像,它们会不必要地使视图混乱,并且可能会因为这样做而禁止我的开发者帐户进入 Play 商店。我也不想将所有这些数据存储在 App-Private 目录中,因为当应用程序被卸载时它会被删除,而且像资源管理器这样的其他应用程序无法访问它们。

我有哪些选择?手动选择文件或目录不是一种选择,除非只需要选择一次目录,然后永久授予访问权限(读取和写入文件)。

我从不在/sdcard/my-app/之外读写。

【问题讨论】:

对于初学者来说仍然有android:requestLegacyExternalStorage模式,虽然这是暂时的,第二种方法是使用SAF,让用户选择根目录并授予您的应用程序访问权限,然后您几乎可以处理复制,移动和其他使用DocumentFile @DarShan 谢谢,android:requestLegacyExternalStorage 在开发过程中会很有用。是否通过 SAF 永久授权访问? android:requestLegacyExternalStorage 也将用于生产,直到下一个主要版本。关于 SAF 权限是永久的,您可以保存在onActivityResult() 中获得的Uri 并使用grantPermission()getContentResolver().takePersistableUriPermission() 是的,Android 11/R 等等。 :) 您的用例是绝对合法的,令人震惊的是,Google 没有人关心这个“功能”会不可逆转地破坏多少应用程序。 【参考方案1】:

在 Android Q 中,对于私有文件夹之外的应用,默认情况下禁用直接文件访问。您可以在以下情况下使用以下几种策略:

    使用 manifest 选项 requestLegacyExternalStorage 来获得旧行为,但它不再适用于 Android R,因此它确实是一个短期解决方案; 使用getExternalFilesDir() 方法保存文件。这是您的私人文件夹,其他应用只有拥有READ_EXTERNAL_STORAGE 权限才能访问这些文件。在这种情况下,最好使用 FileProvider 授予对您文件的其他应用程序的访问权限。 使用StorageManager 类的方法getPrimaryStorageVolume().createOpenDocumentTreeIntent() 请求访问外部主卷。在这种情况下,您需要用户同意并且无论如何您将无法直接使用File api,但是使用DocumentFile 类您有一个非常相似的界面,所以这是更接近旧行为的解决方案。如果您需要在前台和后台执行操作,即无需用户交互,但请求权限的第一次交互除外。

我为第 3 点链接了 Flipper 库,它有助于像在旧版 android 中一样管理文件。

【讨论】:

因此,如果我将文件保存到我的应用程序目录 (context.getExternalFilesDir),我将能够使用 File mFile = new File(PathToExternalFilesDir); 访问该文件?然后,这将在调用 mFile.getAbsoluteFile() 时返回 file:// Uri - 我的应用程序可以使用/使用该文件 uri 吗? 私有目录中允许文件接口 @HB。没错,自 Android-Q 以来,即使没有任何权限,应用程序也可以访问其特定目录。此外,没有其他应用程序可以访问特定的应用程序沙箱目录。查看作用域存储影响汇总表developer.android.com/training/data-storage/files/… 您的第 2 点很棘手。我尝试使用 DownloadManager 并将目标设置为 getExternalFilesDir()。据说下载完成了,但是一旦我检查文件是否存在,它就不存在了。 对不起兄弟!无法理解如何检查文件是否存在,也无法正确理解如何使用 Flipper 编写代码。所以,我拒绝投票这个答案!因为我真的需要帮助..【参考方案2】:

从 Android 11 开始,如果您需要访问外部文件目录,则需要在 Manifest 中添加 MANAGE_EXTERNAL_STORAGE 权限。

应用可以通过执行以下操作请求用户访问所有文件:

1. 在清单中声明MANAGE_EXTERNAL_STORAGE 权限。

2.使用ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION 意图操作将用户引导至系统设置页面,他们可以在其中为您的应用启用以下选项:允许访问以管理所有文件。强>

3.要确定您的应用是否已获得MANAGE_EXTERNAL_STORAGE 权限,请致电Environment.isExternalStorageManager()

在这里阅读here

【讨论】:

这并不完全正确。只要您不在 Play Store 上发布您的应用程序,它就可以工作。但是,如果您确实发布了它,您的应用程序必须属于一组特定的用例才能获得许可。 developer.android.com/training/data-storage/…【参考方案3】:

对于上面的android Q 这是代码可以正常工作以将图像保存在外部存储上 //不要忘记添加Manifest权限 //



        package com.myretro.saveimager;
        
        import androidx.appcompat.app.AppCompatActivity;
        
        import android.content.ContentResolver;
        import android.content.ContentValues;
        import android.graphics.Bitmap;
        import android.graphics.drawable.BitmapDrawable;
        import android.net.Uri;
        import android.os.Build;
        import android.os.Bundle;
        import android.os.Environment;
        import android.provider.MediaStore;
        import android.widget.Button;
        import android.widget.ImageView;
        import android.widget.Toast;
        
        import java.io.File;
        import java.io.FileNotFoundException;
        import java.io.OutputStream;
        
        public class MainActivity extends AppCompatActivity 
            private Button btnsave;
            private ImageView myImage;
        
            @Override
            protected void onCreate(Bundle savedInstanceState) 
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                btnsave = findViewById(R.id.button);
                myImage = findViewById(R.id.imageView);
        
        
                btnsave.setOnClickListener(view -> 
                    BitmapDrawable bitmap1 = (BitmapDrawable) myImage.getDrawable();
                    Bitmap bitmap = bitmap1.getBitmap();
        
                    saveImmageIntoExternalStrogaeaboveQ(bitmap);
                );
            
        
            private void saveImmageIntoExternalStrogaeaboveQ(Bitmap bitmap) 
                OutputStream outputStream;
        
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 
                    ContentResolver contentResolver = getContentResolver();
        
                    ContentValues mValue = new ContentValues();
        
                    mValue.put(MediaStore.MediaColumns.DISPLAY_NAME, "hacker" + ".jpg");
                    mValue.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
                    mValue.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "myimage");
                    Uri imageuri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mValue);
        
                    try 
                        outputStream = contentResolver.openOutputStream(imageuri);
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                        Toast.makeText(this, "Image Saved SuccessFull", Toast.LENGTH_SHORT).show();
                     catch (FileNotFoundException e) 
                        Toast.makeText(this, "Something went wrong " + e.getMessage(), Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    
        
                 else 
                    //old method to write
                
    
      
    
    

【讨论】:

以上是关于Android 10:我都有哪些选择可以将外部存储上的文件保存到名为“/sdcard/my-app/”的目录中的主要内容,如果未能解决你的问题,请参考以下文章

对于浏览器中的可索引本地存储,我都有哪些选择?

使用 Flutter 时我都有哪些存储数据的选项? (iOS和Android)[关闭]

使用 React Native 时我都有哪些存储数据的选项? (iOS和Android)[关闭]

在手机上存储敏感数据都有哪些选择?

实时聊天:我都有哪些选择? [关闭]

当 PowerShell 脚本失败时,我都有哪些选择?