Delphi 10.4.2で作ったAndroidアプリのRequestPermissionsをDelphi 11.0でも動くようにする

2021年11月2日

Androidに実行パーミッションの許可を求めているスマホアプリでは、Delphi 10.4.2 Sydneyでビルドに成功していたプロジェクトでも、そのままではDelphi 11.0 Alexandriaではアプリを作ることができません。この問題を解決するには、下記の三つのことをする必要があります。

・TRequestPermissionsResultEvent と TDisplayRationaleEvent の引数の型を変える
・プロジェクトのライブラリのシステムファイルをデフォルトに戻す
・プロジェクトの出力フォルダ(Android64 または Android32)を削除し、ビルドする

[toc]

TRequestPermissionsResultEvent と TDisplayRationaleEvent の引数の型を変える

Androidに実行パーミッションの許可を要求しているスマホアプリでは、Delphi 10.4.2でビルドに成功していても、Delphi 11.0でビルドすると、下記のエラーが発生します。

[DCC エラー] xxx.pas(lll): E2250 指定された引数で呼び出すことのできるオーバーロード関数 'RequestPermissions’ が定義されていません
[DCC Error] xxx.pas(lll): E2250 There is no overloaded version of 'RequestPermissions’ that can be called with these arguments(英語設定の場合)

Delphi 10.4.2でAndroidのパーミッションを要求するときの標準的なコードは下記の通りです ※1 が、このうち、ハイライトしている3行がこのエラーの原因でした。

procedure TFormMain.RequestPermissions;
begin
{$IFDEF ANDROID}
  FPermission_CAMERA := JStringToString(TJManifest_permission.JavaClass.CAMERA);
  PermissionsService.RequestPermissions([FPermission_CAMERA],
    RequestResult, DisplayRationale)
{$ENDIF}
end;
 
procedure TFormMain.RequestResult(Sender: TObject;
  const APermissions: TArray;
  const AGrantResults: TArray);
begin
  if (AGrantResults[0] = TPermissionStatus.Granted) then
  begin
  end
end;

procedure TFormMain.DisplayRationale(Sender: TObject;
  const APermissions: TArray;
  const APostRationaleProc: TProc);
begin
end;

Delphi 11.0では、引数 APermissions の型を TArray<string> から TClassicStringDynArray に、また引数 AGrantResults の型を TArray<TPermissionStatus> から TClassicPermissionStatusDynArray に変更する必要があります ※2

サンプルコードで示すと、下記の通りです。これで上記エラーは解消します。

procedure TFormMain.RequestPermissions;
begin
{$IFDEF ANDROID}
  FPermission_CAMERA := JStringToString(TJManifest_permission.JavaClass.CAMERA);
  PermissionsService.RequestPermissions([FPermission_CAMERA],
    RequestResult, DisplayRationale)
{$ENDIF}
end;

procedure TFormMain.RequestResult(Sender: TObject;
  const APermissions: TClassicStringDynArray;
  const AGrantResults: TClassicPermissionStatusDynArray);
begin
  if (AGrantResults[0] = TPermissionStatus.Granted) then
  begin
  end
end;

procedure TFormMain.DisplayRationale(Sender: TObject;
  const APermissions: TClassicStringDynArray;
  const APostRationaleProc: TProc);
begin
end;

私のソースでは、ShowMessage, TThread.Synchronize, TTask.Run でも同様の「オーバーロード関数が定義されていません」エラーが発生していましたが、上記の変更だけで、これらのエラーも出なくなりました。

※1:Androidアプリの実行パーミッションを簡単に制御可能な「Mobile Permissions Component」(Embarcadero Japan Support)
※2:Taking To 11… ⇒ Important Breaking Change(Delphi Worlds)

出力フォルダを削除し、ビルドし直す(省略可)

この段階でアプリを実行しても、正しく実行されません。ステップ実行をしようにも、エラーや例外が発生することなく終了してしまいます。これは、classes.dex(JavaをAndroidで実行できるようにしたファイル)が更新されないためです。ビルド ⇒ 実行、またはクリーンアップ ⇒ ビルド ⇒ 実行しても同様で、classes.dex が更新されません。

一度アプリの実行を確認したいとき、或いは、ステップ実行して後述の Java のエラーが発生するところを見たいときなどは、ここで、ターゲットプラットフォームのコンパイラ出力ディレクトリ(Android64 または Android32)を削除し、プロジェクトをビルドし直してください。これにより、classes.dex が更新され、求めている状態になります。

なおこの手順は、「Delphi 10.4.2のAndroidアプリのソースを、Delphi 11.0で動かす」という最終的な目的の観点で言えば、省略可能です。

プロジェクトのライブラリのシステムファイルをデフォルトに戻す

ここまでの変更をしたところで、新たに下記エラーが発生するようになります。

プロジェクト xxx.apk は例外クラス EJNI(メッセージ 'Java の型 Jcontent_ContextCompat が見つかりません’)を送出しました。
Project xxx.apk raised exception class EJNI with message 'Java type Jcontent_ContextCompat could not be found.’(英語設定の場合)

PermissionsService.RequestPermissions から処理を辿って行くと、System.Android.Permissions(C:\Program Files (x86)\Embarcadero\Studio\22.0\source\rtl\common)の123行目(下のコードのハイライト部分)で使われている class が見つけられないと言っているようです。しかし、TJcontent_ContextCompat の定義は見つけられますし、アプリのuses節にも問題はないようでした。

function TAndroidPermissionsService.IsPermissionGranted(const APermission: string): Boolean;
begin
  Result := TJcontent_ContextCompat.JavaClass.checkSelfPermission(TAndroidHelper.Context,
    StringToJString(APermission)) = TJPackageManager.JavaClass.PERMISSION_GRANTED
end;

結論としては、プロジェクトのライブラリのシステムファイルをデフォルトに戻すことで解決します。

具体的には、下の画面の通り、プロジェクトのターゲットプラットフォームで「Android 64 ビット」(または Android 32 ビット)を展開し、「ライブラリ」ノード上でマウスの右ボタンをクリックし、「システム ファイルをデフォルトに戻す」を実行します。

このことは、embarcadero の「RAD Studio 11.0 トピック」の「新機能」の「Android 30 API support」にも下記のように書かれています(下線部分は日本語訳が間違っていたので、私の方で修正しています)。

新しいリリースには異なる Java ライブラリのセットが含まれているため、古いプロジェクトには互換性がありません。古いバージョンの RADStudio でビルドした Android プロジェクトを開く場合は、次のことを行う必要があります:
・ドッキング可能な[プロジェクト]ウィンドウに移動します。
・アクティブ ターゲット プラットフォームとして、Android 32 ビットか Android 64 ビットを選択します。
・ライブラリ ノードを右クリックします。
・メニュー項目[システム ファイルをデフォルトに戻す]をクリックします。

Delphi 11.0でAndroidのパーミッションを要求するときの設定
ライブラリのシステムファイルをデフォルトに戻す

この操作は、プロジェクト配下のことなので、プロジェクトごとに実施する必要があります。また、ターゲットプラットフォームを変更したとき(Android 64 ビット ⇔ Android 32 ビット)にも、再度行う必要があります。

参考までに、この操作前後で、私の環境では、ライブラリ下のファイルが下の画面のように変わりました。操作後の状態は画面に収まりきらないので割愛していますが、70ファイルほどありました。

Delphi 11.0でAndroidのパーミッションを要求するときの設定前後の状態
ライブラリ下のファイルの状態(左:操作前 右:操作後)

出力フォルダを削除し、ビルドし直す

この段階でアプリを実行しても、正しく実行されません。ステップ実行をしようにも、エラーや例外が発生することなく終了してしまいます。これは、classes.dex(JavaをAndroidで実行できるようにしたファイル)が更新されないためです。ビルド ⇒ 実行、またはクリーンアップ ⇒ ビルド ⇒ 実行しても同様で、classes.dex が更新されません。

そこで、ターゲットプラットフォームのコンパイラ出力ディレクトリ(Android64 または Android32)を削除し、プロジェクトをビルドし直してください。これにより、classes.dex が更新されます。

先の手順でフォルダ削除とビルドを実施済なのに、「Java の型 Jcontent_ContextCompat が見つかりません」のエラーが出続ける場合があります。その場合は、クリーンアップのみ実施してください。ここでの再度のフォルダ削除とビルドは不要です。

まとめ

Delphi 10.4.2で動作していた、実行パーミッションの許可を求めているAndroidアプリのプロジェクトを、Delphi 11.0でも使えるようにするには、次の三つの手順を実施してください。

・TRequestPermissionsResultEvent と TDisplayRationaleEvent の引数の型を変える
・プロジェクトのライブラリのシステムファイルをデフォルトに戻す
・コンパイラの出力フォルダ(Android64 または Android32)を削除し、ビルドする

これにより、Delphi 11.0でもビルドが成功し、Android上にアプリを配置、実行できるようになります。

なお、私の環境では、RAD Studio 11.0をインストールしたときの状態のまま、AdoptOpenJDK OpenJDK 8 と Android SDK 25.2.5 とを使っていましたが、そこは原因ではありませんでした。