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

Android申请CALL_PHONE权限后立即执行ACTION_CALL intent的实现问题

问题原因

ActivityCompat.requestPermissions 是异步调用方法,触发系统权限弹窗后会立刻返回,不会阻塞等待用户完成权限选择操作,因此你在请求后立刻执行的权限校验逻辑无法拿到用户刚刚的授权结果,才会出现需要点击两次才能拨号的问题。

解决步骤

  1. 移除点击事件中requestPermissions之后冗余的权限校验+拨号逻辑,这段代码不会在首次授权后生效。
  2. 修正权限说明弹窗的确认按钮逻辑:用户点击确认后再发起权限申请,避免弹窗还没点就先弹出系统权限请求。
  3. 在发起权限请求的Activity/Fragment中重写onRequestPermissionsResult回调,这个方法会在用户完成权限选择后自动触发,在回调中判断授权结果,授权通过就直接执行拨号逻辑。

完整代码示例

1. 修改后的点击事件代码

// 所有代码位于点击事件监听器内部,number变量已提前赋值
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse(String.format("tel:%s", Uri.encode(number))));

if(ActivityCompat.checkSelfPermission(activity, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED){
    activity.startActivity(callIntent);
} else {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if(activity.shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)){
            final String permissionTip = "拨号权限仅用于拨打您指定的号码,不会收集或用于其他用途";
            new AlertDialog.Builder(activity)
                    .setTitle("需要拨号权限")
                    .setMessage(permissionTip)
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                            // 用户确认知晓后发起权限申请
                            ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CALL_PHONE}, DIAL_PERMISSION_REQUEST_CODE);
                        }
                    })
                    .setNegativeButton("取消", null)
                    .create().show();
            // 直接返回,避免后续重复发起权限申请
            return;
        }
    }
    // 首次申请权限,直接触发系统弹窗
    ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CALL_PHONE}, DIAL_PERMISSION_REQUEST_CODE);
}

2. 重写Activity的权限回调方法

// 此处的number可提前存储为Activity的成员变量,方便回调中直接调用
private String targetDialNumber;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // 匹配拨号权限的请求码
    if (requestCode == DIAL_PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 授权成功,直接执行拨号
            Intent callIntent = new Intent(Intent.ACTION_CALL);
            callIntent.setData(Uri.parse(String.format("tel:%s", Uri.encode(targetDialNumber))));
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                startActivity(callIntent);
            }
        } else {
            // 授权失败,可自定义提示
            Toast.makeText(this, "未获取拨号权限,无法完成拨号操作", Toast.LENGTH_SHORT).show();
        }
    }
}

注意事项

  • 如果是在Fragment中发起权限申请,请调用Fragment自身的requestPermissions方法,否则回调只会触发宿主Activity的onRequestPermissionsResult,不会走到Fragment的回调中。
  • 拨号前需要提前将目标号码存储为类的成员变量,避免回调时无法获取要拨打的号码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 04:36:03