从 Android11 开始,我是否需要遵守 Android 的 SAF 才能在 public 下载位置创建文件?

Starting from Android11, do I need to comply to Android's SAF just to even create a file at public Download location?

我在 Android 11。我只想能够在 phone 的 /storage/emulated/0/Download 目录中创建一个文件。因此,这是我需要在其中创建文件的 public 下载文件夹。

我不需要访问 /storage/emulated/0/Download 中的 read/modify/delete 个其他文件。我只希望我的应用程序能够在 /storage/emulated/0/Download 创建和替换自己的文件。就是这样!

问题:

我的应用程序能够使用 Android 10 在 /storage/emulated/0/Download 处创建一个文件,但自 Android 11.

以来它无法做到这一点

我的用例非常简单。我只想在 /storage/emulated/0/Download 创建一个文件,并通过不时用新数据替换它来不断向文件附加更多数据。

运行 Android 11 上的以下代码对我来说失败了:

File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new File(path, "Somefile.txt");
String location = file.getAbsolutePath();

try {
     file.createNewFile();
     byte[] data1 = {1,1,0,0};
     OutputStream fo = new FileOutputStream(file);
     fo.write(data1);
     fo.close();
     Log.i(TAG, "The file is created at " + location);
 } catch (IOException e) {
     Log.e(TAG, "Exception creating file");
     return location;
 }

Do I need to go by the Storage Access Framework for this?

没有

Do I need any special permission for this?

不,尽管您需要 WRITE_EXTERNAL_STORAGE 用于较旧的设备。

Or is it possible get access to /storage/emulated/0/Documents without going through the hassle of Android SAF?

Environment.getExternalStoragePublicDirectory() is being undeprecated, so it is safe to use that to access Environment.DIRECTORY_DOWNLOADS. That should give you the same access that you have with MediaStore:

  • 您可以在那里创建文件

  • 您可以在那里读写自己的文件1

  • 您无权访问其他应用程序的文件

1 就本项目符号而言,“您自己的”是指您当前的应用程序安装——如果用户卸载并重新安装您的应用程序,您的旧安装中的文件应用程序不再被视为“您自己的”。

因此,例如,这个 activity 在 targetSdkVersion 31 项目中没有任何权限也能正常工作:

package com.commonsware.downloadtest;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    writeToDownloads();
  }

  private void writeToDownloads() {
    try {
      File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
      File target = new File(dir, "test.txt");
      PrintWriter out = new PrintWriter(new FileWriter(target));

      out.println("hello, world!");
      out.flush();
      out.close();
    } catch (Exception e) {
      Log.e("DownloadTest", "Exception writing to Downloads/", e);
    }
  }
}

现在,这段代码 糟透了 ,因为它不处理旧设备,在主应用程序线程上执行 I/O 等。但是,它写入数据到指定文件,三星A50 运行 Android 11.

请注意,如果文件已经存在,这将给出一个异常。所以,例如,我认为这个例子不起作用,直到我意识到我已经在 Downloads/ 中得到了 test.txt ,这是我一个月前做的一些实验。一旦我删除了那个文件,样本就可以正常工作了。