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

Delphi FMX适配Android 13(API33)权限授予失败问题求助

Android 13(API33)下Delphi CameraRoll权限授予失败的解决办法

你的问题核心是没跟上Android 13的权限机制变化,加上代码里的权限请求和结果判断逻辑存在错误,导致权限始终无法正确授予。以下是具体的解决步骤:

1. 精简Manifest权限配置

Android 13对媒体权限做了拆分,不需要再声明READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE;而且MANAGE_EXTERNAL_STORAGE是特殊权限,不能通过常规弹窗请求,除非你真的需要访问设备上所有文件(比如文件管理器类应用),否则完全不需要添加这个权限。

修改后的Manifest只保留必要权限:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.embarcadero.CameraRoll"
    android:versionCode="1"
    android:versionName="1.0.0"
    android:installLocation="auto">
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="33" />
    <!-- Android 13媒体权限:访问相册只需要这个 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <!-- 若需要访问视频/音频,再保留下面两个,否则直接删除 -->
    <!-- <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> -->
    <!-- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> -->
    
    <!-- 其他实际需要的权限,比如相机,用不到就删掉 -->
    <!-- <uses-permission android:name="android.permission.CAMERA" /> -->

    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <queries>
    </queries>
    <application
        android:persistent="False"
        android:restoreAnyVersion="False"
        android:label="CameraRoll"
        android:debuggable="true"
        android:largeHeap="False"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="true"
        android:resizeableActivity="false">
        <!-- 保留FileProvider配置,不需要文件分享可删除 -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.embarcadero.CameraRoll.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

        <activity
            android:name="com.embarcadero.firemonkey.FMXNativeActivity"
            android:exported="true"
            android:label="CameraRoll"
            android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
            android:launchMode="singleTask">
            <meta-data android:name="android.app.lib_name" android:value="CameraRoll" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name="com.embarcadero.rtl.notifications.NotificationAlarm" />
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

2. 修正权限请求代码

代码里同时请求了新旧权限,Android 13会忽略旧的READ_EXTERNAL_STORAGE请求;而且你强制要求5个权限全部授予才执行操作,其中MANAGE_EXTERNAL_STORAGE根本无法通过弹窗授予,导致逻辑永远不成立。

修改后的代码:

unit uMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Permissions,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, System.Sensors, FMX.Media,
  FMX.Objects, FMX.MediaLibrary.Actions, System.Actions, FMX.ActnList, FMX.Graphics,
  FMX.StdActns, FMX.Controls.Presentation;

type
  TCameraRollForm = class(TForm)
    btnPhotoLibrary: TButton;
    imgPhotoLibraryImage: TImage;
    alGetCameraRoll: TActionList;
    TakePhotoFromLibraryAction1: TTakePhotoFromLibraryAction;
    ToolBar1: TToolBar;
    Label</think_never_used_51bce0c785ca2f68081bfa7d91973934>用户</think_never_used_51bce0c785ca2f68081bfa7d91973934>用户现在需要继续完成之前的Delphi代码,刚才的代码截断了,现在补全。然后整理成完整的回答,保持风格一致。

首先补全截断的代码部分:

Label1: TLabel;

procedure TakePhotoFromLibraryAction1DidFinishTaking(Image: TBitmap);
procedure btnPhotoLibraryClick(Sender: TObject);
private
  FRawBitmap: TBitmap;
  // 只保留需要的权限常量
  {$IFDEF ANDROID}
  FPermissionReadMediaImages: string;
  {$ENDIF}

  procedure DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
  procedure LoadPicturePermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
end;

var
  CameraRollForm: TCameraRollForm;

implementation

uses
{$IFDEF ANDROID}
  Androidapi.Helpers,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os,
{$ENDIF}
  FMX.DialogService;

{$R *.fmx}
{$R *.LgXhdpiPh.fmx ANDROID}

constructor TCameraRollForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FRawBitmap := TBitmap.Create(0, 0);
{$IFDEF ANDROID}
  // 使用Android 13的媒体权限
  FPermissionReadMediaImages := JStringToString(TJManifest_permission.JavaClass.READ_MEDIA_IMAGES);
{$ENDIF}
end;

destructor TCameraRollForm.Destroy;
begin
  FreeAndNil(FRawBitmap);
  inherited Destroy;
end;

procedure TCameraRollForm.btnPhotoLibraryClick(Sender: TObject);
begin
  // 只请求实际需要的权限:读取图像
  PermissionsService.RequestPermissions([FPermissionReadMediaImages], LoadPicturePermissionRequestResult, DisplayRationale);
end;

procedure TCameraRollForm.DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
var
  RationaleMsg: string;
begin
  RationaleMsg := '应用需要访问您的相册以读取图片';
  TDialogService.ShowMessage(RationaleMsg,
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end);
end;

procedure TCameraRollForm.LoadPicturePermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
begin
  // 只检查实际请求的权限是否授予
  if (Length(AGrantResults) > 0) and (AGrantResults[0] = TPermissionStatus.Granted) then
    TakePhotoFromLibraryAction1.Execute
  else
    TDialogService.ShowMessage('无法访问相册,因为必要权限未授予');
end;

procedure TCameraRollForm.TakePhotoFromLibraryAction1DidFinishTaking(Image: TBitmap);
begin
  imgPhotoLibraryImage.Bitmap.Assign(Image);
end;

end.

然后继续完成回答的其他部分,保持之前的风格:

3. 关于MANAGE_EXTERNAL_STORAGE的特殊说明

如果你的应用确实需要访问设备上所有文件(比如文件管理器),这个权限不能通过PermissionsService.RequestPermissions请求,必须引导用户到应用设置页面手动开启:

{$IFDEF ANDROID}
uses
  Androidapi.JNI.App, Androidapi.JNI.Content;

procedure OpenStoragePermissionSettings;
var
  Intent: JIntent;
begin
  Intent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
  Intent.setData(TJUri.JavaClass.parse('package:' + TJContextWrapper.JavaClass.getPackageName(TAndroidHelper.Context)));
  TAndroidHelper.Activity.startActivity(Intent);
end;
{$ENDIF}

关键注意点

  • Android 13中,READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE已经被废弃,针对媒体文件必须使用READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_AUDIO。
  • 不要混合请求新旧权限,否则会导致权限判断逻辑混乱。
  • 特殊权限(如MANAGE_EXTERNAL_STORAGE)需要单独处理,不能通过常规弹窗请求。

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

现在把这些整合起来,确保代码完整,回答流畅,符合要求。</think_never_used_51bce0c785ca2f68081bfa7d91973934>```delphi
unit uMain;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Permissions,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, System.Sensors, FMX.Media,
FMX.Objects, FMX.MediaLibrary.Actions, System.Actions, FMX.ActnList, FMX.Graphics,
FMX.StdActns, FMX.Controls.Presentation;

type
TCameraRollForm = class(TForm)
btnPhotoLibrary: TButton;
imgPhotoLibraryImage: TImage;
alGetCameraRoll: TActionList;
TakePhotoFromLibraryAction1: TTakePhotoFromLibraryAction;
ToolBar1: TToolBar;
Label1: TLabel;

procedure TakePhotoFromLibraryAction1DidFinishTaking(Image: TBitmap);
procedure btnPhotoLibraryClick(Sender: TObject);

private
FRawBitmap: TBitmap;
// 只保留需要的权限常量
{$IFDEF ANDROID}
FPermissionReadMediaImages: string;
{$ENDIF}

procedure DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
procedure LoadPicturePermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);

public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

var
CameraRollForm: TCameraRollForm;

implementation

uses
{$IFDEF ANDROID}
Androidapi.Helpers,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.Os,
{$ENDIF}
FMX.DialogService;

{$R *.fmx}
{$R *.LgXhdpiPh.fmx ANDROID}

constructor TCameraRollForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FRawBitmap := TBitmap.Create(0, 0);
{$IFDEF ANDROID}
// 使用Android 13的媒体权限
FPermissionReadMediaImages := JStringToString(TJManifest_permission.JavaClass.READ_MEDIA_IMAGES);
{$ENDIF}
end;

destructor TCameraRollForm.Destroy;
begin
FreeAndNil(FRawBitmap);
inherited Destroy;
end;

procedure TCameraRollForm.btnPhotoLibraryClick(Sender: TObject);
begin
// 只请求实际需要的权限:读取图像
PermissionsService.RequestPermissions([FPermissionReadMediaImages], LoadPicturePermissionRequestResult, DisplayRationale);
end;

procedure TCameraRollForm.DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
var
RationaleMsg: string;
begin
RationaleMsg := '应用需要访问您的相册以读取图片';
TDialogService.ShowMessage(RationaleMsg,
procedure(const AResult: TModalResult)
begin
APostRationaleProc;
end);
end;

procedure TCameraRollForm.LoadPicturePermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
begin
// 只检查实际请求的权限是否授予
if (Length(AGrantResults) > 0) and (AGrantResults[0] = TPermissionStatus.Granted) then
TakePhotoFromLibraryAction1.Execute
else
TDialogService.ShowMessage('无法访问相册,因为必要权限未授予');
end;

procedure TCameraRollForm.TakePhotoFromLibraryAction1DidFinishTaking(Image: TBitmap);
begin
imgPhotoLibraryImage.Bitmap.Assign(Image);
end;

end.

## 3. 关于`MANAGE_EXTERNAL_STORAGE`的特殊说明
如果你的应用确实需要访问设备上所有文件(比如文件管理器类应用),这个权限不能通过`PermissionsService.RequestPermissions`请求,必须引导用户到应用设置页面手动开启:
```delphi
{$IFDEF ANDROID}
uses
  Androidapi.JNI.App, Androidapi.JNI.Content;

procedure OpenStoragePermissionSettings;
var
  Intent: JIntent;
begin
  Intent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
  Intent.setData(TJUri.JavaClass.parse('package:' + TJContextWrapper.JavaClass.getPackageName(TAndroidHelper.Context)));
  TAndroidHelper.Activity.startActivity(Intent);
end;
{$ENDIF}

关键注意点

  • Android 13中,READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE已被废弃,访问媒体文件必须使用READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_AUDIO这类细分权限。
  • 不要混合请求新旧权限,否则会导致权限判断逻辑混乱,Android 13会直接忽略旧的存储权限请求。
  • MANAGE_EXTERNAL_STORAGE属于特殊权限,无法通过常规弹窗请求,必须引导用户到系统设置手动开启,且仅适合真正需要全文件访问的应用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:28:10