Android 13 - 如何请求WRITE_EXTERNAL_STORAGE权限

76

我的Android应用针对Android 13 (API 33)进行开发。

WRITE_EXTERNAL_STORAGE权限在API 33以下的版本(即Android 12及以下)似乎可以正常工作,但当在Android 13上运行应用时,WRITE_EXTERNAL_STORAGE的运行时权限弹出框不会出现。

我的Android应用程序在应用程序私有存储中创建一个密钥库文件。

关于Android 13的行为更改,官方文档提到:

如果您的应用程序针对Android 13进行开发,则必须请求一个或多个新权限,而不是READ_EXTERNAL_STORAGE。

新的权限包括:

  • 图像和照片:READ_MEDIA_IMAGES
  • 视频:READ_MEDIA_VIDEO Audio
  • 音频文件:READ_MEDIA_AUDIO

我在官方文档中没有找到任何相关信息。该文档仅侧重于媒体文件,没有涉及其他文件类型。

https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions


3
我的Android应用程序在应用的私有存储中创建一个密钥库文件。不需要任何权限。 - blackapps
请仔细阅读您的链接:“如果您的应用程序针对Android 13或更高版本,并且需要访问其他应用程序创建的媒体文件”,因此,如果您想在外部存储上创建文件,则仍应请求写入外部存储权限。我不知道为什么没有更多的用户交互。但更重要的是:您是否获得了外部存储(而不是应用程序私有)的写入访问权限? - blackapps
一样的问题。我的应用程序出现了一个巨大的错误,因为创建文件的场景检查权限并且用户无法进行下一步操作。 - brucemax
请查看此答案,因为它有一些很好的观点:https://dev59.com/ulEG5IYBdhLWcg3wIksH#73630987 - Top4o
6个回答

67

简而言之:你不需要请求WRITE_EXTERNAL_STORAGE权限。

来自Google的官方文档:

"如果您的应用程序面向Android 11,则WRITE_EXTERNAL_STORAGE权限和WRITE_MEDIA_STORAGE特权权限都不再提供任何额外访问权限。"

因此,实际上,在Android 11或更高版本上请求WRITE_EXTERNAL_STORAGE权限时,您将请求无效。这就是Android迁移到作用域存储的全部意义,其有效地防止应用程序读取或写入其他应用程序的存储目录,除非它们正在访问特定的文件类型(例如使用您提到的权限的媒体)或是通过Google授予的特殊文件管理器权限。有关作用域存储的更多见解,请参见这篇文章,但简而言之,安全性和应用程序卸载后剩余文件是Google这样做的重要原因。

如果您真的想要编写文件,要么确保只写入您应用程序的指定存储目录,在这种情况下,您根本不需要任何权限;要么,如果您确实需要写入您的应用程序没有所有权的目录,请从Google获取文件管理器权限(如何获取该权限)。


51
补充一下,如果你尝试在Android 13+上检查或请求WRITE_EXTERNAL_STORAGE权限,它将始终返回false。因此,在Android 13+上您必须完全跳过权限检查/请求。 - Lifes
1
@Lifes 我认为它在 Android 11+ 上始终会返回 false,而不仅仅是在 Android 13+ 上。 - Hisham Hijjawi
@HishamHijjawi 我使用 ContextCompat.checkSelfPermission 检查了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。在 A12 上返回了 true(至少在我调用 requestPermissions 并接受存储权限请求后是这样的)。但是在 A13 上返回了 false,当我调用 requestPermissions 时,根本没有存储权限请求弹出窗口。我还使用了 requestLegacyExternalStorage,但这不应该影响此行为,因为自 A11 版本以来应完全忽略 requestLegacyExternalStorage - JustAMartin
我需要创建文件,但是出现了EACCES错误(权限被拒绝)。 - Muhammed shamshad p
但是媒体商店呢?它能帮助将PDF文件保存到下载目录中吗? - Waldmann

4
您可以在Android官方文档或"在Android 13中如何保存文件到共享存储位置"中找到访问非媒体文件的文档。从Android 10开始,如果您想写一个文件,使其他应用程序(例如文件管理器)或用户直接访问该文件,则必须将其写入共享存储位置。这需要分3个步骤来完成:第1步:启动系统选择器,由用户选择目标位置。
private ActivityResultLauncher<Intent> launcher; // Initialise this object in Activity.onCreate()
private Uri baseDocumentTreeUri;
public void launchBaseDirectoryPicker() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
        launcher.launch(intent);
 }

步骤2:在onActivityResult()方法中,接收系统选择器返回的目标Uri。在这里,你可以选择性地保存权限和Uri以备将来使用。

@Override
    public void onActivityResult(ActivityResult result) {
        if (result.getResultCode() == Activity.RESULT_OK) {
            baseDocumentTreeUri = Objects.requireNonNull(result.getData()).getData();
            final int takeFlags = (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

            // take persistable Uri Permission for future use
            context.getContentResolver().takePersistableUriPermission(result.getData().getData(), takeFlags);
            SharedPreferences preferences = context.getSharedPreferences("com.example.fileutility", Context.MODE_PRIVATE);
            preferences.edit().putString("filestorageuri", result.getData().getData().toString()).apply();
        } else {
            Log.e("FileUtility", "Some Error Occurred : " + result);
        }
}

步骤三:将内容写入文件。

public void writeFile(String fileName, String content)  {
        try {
            DocumentFile directory = DocumentFile.fromTreeUri(context, baseDocumentTreeUri);
            DocumentFile file = directory.createFile("text/*", fileName);
            ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(file.getUri(), "w");
            FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
            fos.write(content.getBytes());
            fos.close();
        } catch (IOException e) {
            
        }
}

2

如果您正在使用sdk 33,请使用( READ_MEDIA_IMAGES )而不是( WRITE_EXTERNAL_STORAGE )。


但是读取只是读取,这能授予写入权限吗?我怀疑。 - Csaba Toth

1
这对我在React Native上起作用。
  // other imports
 import DeviceInfo from "react-native-device-info"

  // other states
  const [apiLevel, setAPILevel] = useState(null);

  //update your state variable
  useEffect(()=>{
  const getDeviceInfo = async()=>{
    const apiLevel = DeviceInfo.getSystemVersion();
    setAPILevel(apiLevel);
    console.log(" ~ file: app.tsx:115 ~ getDeviceInfo ~ apiLevel:", 
    apiLevel)
      
   }
  getDeviceInfo();
 },[])


if (Platform.OS === "android" && apiLevel < 13) {
        console.log(" ~ file: SingleCategoryScreen.tsx:327 ~ 
   downloadAllLectureVideos ~ Platform.OS:", Platform.OS)
        const granted = await PermissionsAndroid.request(
          PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
        )
        if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
          console.log("Permission denied. Cannot download videos.");
          setIsDownloading(false)
          return
        }
      }

0
检查 WRITE_EXTERNAL_STORAGE 适用于 Android API 31 及以上版本。


    public static boolean isGrantedPermissionWRITE_EXTERNAL_STORAGE(Activity activity) {
        int version = Build.VERSION.SDK_INT;
        if( version <= 32 ) {
            boolean isAllowPermissionApi28 = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ;
            Log.i("general_em","isGrantedPermissionWRITE_EXTERNAL_STORAGE() - isAllowPermissionApi28: " + isAllowPermissionApi28);
            return  isAllowPermissionApi28;
        } else {
            boolean isAllowPermissionApi33 = Environment.isExternalStorageManager();
            Log.i("general_em","isGrantedPermissionWRITE_EXTERNAL_STORAGE() - isAllowPermissionApi33: " + isAllowPermissionApi33);
            return isAllowPermissionApi33;
        }
    }


-4

对于 Android 13 及以上版本,请使用以下权限:

<!-- Required only if your app needs to access images or photos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

请参考下面的参考资料。

https://developer.android.com/training/data-storage/shared/media


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接