提问者:小点点

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


在Android Pie之前,我总是将应用程序需要存储的文件存储在< code>/sdcard/my-app/上,这是通过

文件fBaseDir=新文件(Environment.getExextalStorageDirectory(),"my-app");

我的一个应用程序在< code >/SD card/my-App/favicons 中存储了数百个(多达数千个)favicon,当用户创建应用程序的备份时,它会存储在< code >/SD card/my-App/backups 中。Google Chrome在与我的应用程序共享时拍摄的网页截图存储在< code >/SD card/my-app/screens 中,等等。

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

我还可以通过将/sdcard/my-app/目录复制到我的PC来轻松创建备份,并在另一部手机上恢复它,例如当我迁移到新设备时。

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

在Android 10中,这种方法已经崩溃。我无法将收藏夹图标添加到Images Media Folder,因为它们不是真实的图像,它们会不必要地扰乱视图,并可能因此而禁止我的开发者帐户进入Play Store。我也不想将所有这些数据存储在App Private目录中,因为当应用被卸载时,这些数据会被删除,而且其他应用(如浏览器)无法访问这些数据。

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

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


共3个答案

匿名用户

在Android Q中,默认情况下,对其私人文件夹之外的应用程序禁用直接文件访问。以下是您可以在您的案例中使用的几个策略:

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

我链接第3点的Flipper库,它有助于管理旧Android版本中的文件。

匿名用户

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

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

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

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

3.要确定您的应用程序是否已被授予< code > MANAGE _ EXTERNAL _ STORAGE 权限,请调用< code > environment . isexternalstoragemanager()。

在这里读它

匿名用户



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