编辑:对不起,我没有在FireMonkey上做更多的功课。这就是我坚持自己的主题,而不是属于它的主题。我已经添加了这些内容,试图让我的答案更值得赏金。

如果您可以将应用清单上的targetSdk限制为22(5.1 Lollipop),则用户必须在安装时授予权限,以便HasPermission永远不会返回false。 (不确定FireMonkey是如何工作的。)

如果您想使用Marshmallow +中的动态权限功能,以下是我从this page收集的一些信息:

您需要有权访问Activity回调方法onRequestPermissionsResult。以下是您必须跳过的所有箍:

使用开源工具Dex2Jar将Delphi中的Android classes.dex文件转换回Java,以便您可以针对FMXNativeActivity类进行编译。

在Java中编写FMXNativeActivity的子类,用于定义native方法(让我们称之为onRequestPermissionsResultNative,并覆盖onRequestPermissionsResult方法以调用原生方法。

运行javac以获取包含子类的.class文件

运行jar将.class文件放入.jar文件

运行dx.bat将您的.jar文件转换为Android .dex文件

运行DexMerger将您的.dex文件合并到Delphi的classes.dex文件中

现在剩下要做的就是编写一些棘手的Delphi代码来定义你的onRequestPermissionsResultNative方法并将其注册到JNI环境。哦,不要忘记用原生方法切换到正确的线程。

我引用的链接显示了如何使用onActivityResult执行此操作。您必须针对其他方法调整这些步骤。

我甚至没有谈过如何处理操作系统暂停你的应用程序以询问用户的权限并在之后恢复它。

让这成为所有人的一课:不要相信跨平台工具;你会失望的。

我在Java而不是Delphi工作,所以你必须在这里做一点推断。

与您一样,我必须获取IMEI号码,系统对话框会询问用户:"允许应用制作和管理电话?"我需要向用户解释该应用只是获取设备ID并且不打算制作或管理电话。所以

检查您是否拥有该权限

如果您未获得许可,请检查是否应显示解释

如果您不需要显示说明,请启动权限请求操作

我应该提到shouldShowRequestPermissionRationale和requestPermissions是Activity类的方法。
private static final int READ_PHONE_STATE_PERMISSIONS_REQUEST = 2;
private boolean mHasReadRationale;
void doPermissionsStuff() {
// version checking code omitted, this block runs for marshmallow and later
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
// do the operation that needs the permission here
} else {
// the flag indicates if the rationale dialog has already been displayed
if (! mHasReadRationale && shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_STATE)) {
// pop a dialog that explains what's going on to the user
} else {
requestPermissions(new String[] {Manifest.permission.READ_PHONE_STATE}, READ_PHONE_STATE_PERMISSIONS_REQUEST);
}
}
}

在此对话框的正面按钮(即用户想要继续)中,将mHasReadRationale标志设置为true并再次呼叫doPermissionsStuff。 (对于取消,我将用户发送回上一个屏幕。)

要获得requestPermissions操作的结果,您需要覆盖Activity的{{1}}方法:

onRequestPermissionsResult

显然,当系统要求用户提供权限时,它会停止您的应用,因此您无法在此时显示用户界面,告诉用户您没有权限。所以我设置了一个标志,当应用程序恢复时,然后我告诉用户该应用程序没有权限进行操作。

private boolean mPermissionDenied;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case READ_PHONE_STATE_PERMISSIONS_REQUEST:
// I'm only checking for one permission, so I make assumptions here
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// you can do the operation that needs the permission now
} else {
mPermissionDenied = true; // set a flag for checking later
}
}
}

以下是一个示例流程:

用户需要免费试用和应用程序需要获取IMEI,以便他们不能一遍又一遍地获得免费试用,jeez。应用程序调用{{1}}。

应用程序调用{{1}}并确定尚未授予权限

应用程序调用{{1}}。根据我的经验,@Override

protected void onResumeFragments() {
super.onResumeFragments();
if (mPermissionDenied) {
// show dialog to the user that the app can't do the operation because it doesn't have permission
mPermissionDenied = false;
}

}仅在用户拒绝一次权限后才返回true。因此,您还没有向用户显示基本用户界面。

应用程序调用{{1}}

系统会询问用户"允许应用程序拨打和管理电话?"

用户认为这是WAAAAAAY太可怕了,按下否按钮。

使用拒绝结果调用

doPermissionsStuff()并设置checkSelfPermission()。

shouldShowRequestPermissionRationale()被调用,并向用户显示一个对话框,表明他们无法获得免费试用,因为该应用没有获得许可。

用户决定再试一次。 shouldShowRequestPermissionRationale()被称为。{/ li>

应用程序调用{{1}}并且(再次)确定尚未授予权限

应用程序调用{{1}}。这次它返回true。

应用程序向用户显示一个平静和舒缓的消息,不,我们不会接管您的手机,我们只是想要这个怪胎' IMEI号码,全部,如果您不允许该应用访问IMEI,您将无法获得免费试用。我必须在某处绘制行。

用户按下继续,因此requestPermissions()标志设置为true,并再次调用onRequestPermissionsResult()方法。

应用程序调用{{1}}并猜测是什么?许可尚未授予

由于设置了标记,因此用户无法获得基本原理UI。

应用程序调用{{1}}

系统会询问用户"允许应用程序拨打和管理电话?"

用户将自己命名为命运,然后按是。

使用授予的结果调用

mPermissionDenied,免费试用注册将继续进行。