You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 11中TDirectory.GetFiles()在Android共享存储返回空列表问题

Delphi 11 Android共享存储访问问题解决方案

问题现象

在Delphi 10.4中可正常获取Android设备共享存储的文件列表,但升级到Delphi 11后,即便已配置必要权限(代码或设备端设置),仍出现以下异常:

  • TDirectory.GetFiles()返回空列表
  • TDirectory.GetDirectories()可正常工作
  • TFile.Exists()能识别文件存在,但调用TFile.ReadAllText()时提示权限拒绝

问题根源

  1. Android权限机制变更:Delphi 11默认以Android 11(API 30)及以上版本为目标平台,Android 11开始限制了READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE权限的作用范围,这类权限不再授予对共享存储的全面访问权。
  2. 异步权限请求逻辑错误:原代码中在调用PermissionsService.RequestPermissions后立刻判断权限状态,但权限请求是异步操作,此时回调尚未执行,fOK的初始值会导致逻辑判断失效。

解决方案

1. 修复异步权限请求逻辑

将文件操作逻辑移至权限回调方法中,确保在权限确认授予后再执行文件访问操作。

2. 适配Android 11+的存储权限

  • 媒体文件访问:如果仅需访问图片、音频、视频等媒体文件,Android 13+可替换旧权限为READ_MEDIA_IMAGES、READ_MEDIA_AUDIO、READ_MEDIA_VIDEO;Android 11-12可保留READ_EXTERNAL_STORAGE并配合媒体存储API使用。
  • 全文件访问:如果需要访问所有类型的文件,需请求MANAGE_EXTERNAL_STORAGE权限,同时在AndroidManifest.xml中添加以下声明:
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    
    注意:该权限需要引导用户到应用设置页面手动授权,无法通过代码直接触发弹窗授权。

3. 修改后的示例代码

uses
  System.Types, System.Classes, System.SysUtils, System.IOUtils, System.Permissions,
{$IFDEF ANDROID}
  Androidapi.Helpers,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os,
  Androidapi.JNI.App,
{$ENDIF}
  FMX.Types, FMX.Forms, FMX.Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    procedure PermissionsResult(Sender: TObject;
      const APermissions: TClassicStringDynArray;
      const AGrantResults: TClassicPermissionStatusDynArray);
    procedure HandleFileAccess;
  end;

var
  Form1: TForm1;

implementation
{$R *.fmx}

procedure TForm1.PermissionsResult(Sender: TObject;
  const APermissions: TClassicStringDynArray;
  const AGrantResults: TClassicPermissionStatusDynArray);
var
  n: Integer;
  AllGranted: Boolean;
begin
  AllGranted := True;
  if Length(AGrantResults) > 0 then
    for n := 0 to Length(AGrantResults) - 1 do
      if AGrantResults[n] <> TPermissionStatus.Granted then
      begin
        AllGranted := False;
        Break;
      end;

  if AllGranted then
    HandleFileAccess
  else
    ShowMessage('权限被拒绝');
end;

procedure TForm1.HandleFileAccess;
var
  DataDir: string;
  list: TStringList;
  LList: TStringDynArray;
  I: Integer;
begin
  list := TStringList.Create;
  try
    DataDir := TPath.GetSharedDocumentsPath;
    ShowMessage(DataDir);
    
    // 适配Android 11+:如果是媒体文件,建议使用MediaStore API获取
    LList := TDirectory.GetFiles(DataDir);
    if Length(LList) > 0 then
      for I := 0 to Length(LList) - 1 do
        list.Add(LList[I]);

    ShowMessage(IntToStr(list.Count) + ' 个文件:' + #13#10 + list.Text);
  finally
    list.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Permissions: TArray<string>;
{$IFDEF ANDROID}
  SDKVersion: Integer;
{$ENDIF}
begin
{$IFDEF ANDROID}
  SDKVersion := TJBuild_VERSION.JavaClass.SDK_INT;
  if SDKVersion >= 33 then
    // Android 13+ 媒体文件权限
    Permissions := [JStringToString(TJManifest_permission.JavaClass.READ_MEDIA_IMAGES),
                    JStringToString(TJManifest_permission.JavaClass.READ_MEDIA_AUDIO),
                    JStringToString(TJManifest_permission.JavaClass.READ_MEDIA_VIDEO)]
  else if SDKVersion >= 30 then
    // Android 11-12 使用旧存储权限
    Permissions := [JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE)]
  else
    // Android 10及以下
    Permissions := [JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE),
                    JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE)];
{$ELSE}
  Permissions := [];
{$ENDIF}

  if Length(Permissions) > 0 then
    PermissionsService.RequestPermissions(Permissions, PermissionsResult, nil)
  else
    HandleFileAccess;
end;

end.

全文件访问权限引导代码

如果需要访问非媒体类文件(如文档、压缩包等),Android 11+下可通过以下代码引导用户到设置页面授权:

{$IFDEF ANDROID}
if not TJEnvironment.JavaClass.isExternalStorageManager then
begin
  var Intent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
  var Uri := TJUri.JavaClass.fromString('package:' + TAndroidHelper.Context.getPackageName);
  Intent.setData(Uri);
  TAndroidHelper.Activity.startActivity(Intent);
end;
{$ENDIF}

内容的提问来源于stack exchange,提问作者user20492839

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:15:14