如何请求允许从 Android 版本 Marshmallow 开始从 Android 进行 phone 调用?

How to ask permission to make phone call from Android from Android version Marshmallow onwards?

我正在尝试从 Android 进行 phone 调用,并且我还设置了 运行 时间权限。并询问是否允许进行 phone 调用。但是当我按下允许时,应用程序崩溃了:

我是这样实现的:

private static final int REQUEST_PHONE_CALL = 1;
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},REQUEST_PHONE_CALL);
    }
    else
    {
        startActivity(intent);
    }
}
else
{
    startActivity(intent);
}


@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_PHONE_CALL: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startActivity(intent);
            }
            else
            {

            }
            return;
        }
    }
}

这是我在logcat中获得的:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, 
     request=1, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} 
     to activity {com.devpost.airway/com.devpost.airway.activities.MainActivity}: 
     java.lang.NullPointerException: Attempt to invoke virtual method 
     'java.lang.String android.content.Intent.toString()' on a null object reference

      at android.app.ActivityThread.deliverResults(ActivityThread.java:3733)
      at android.app.ActivityThread.handleSendResult(ActivityThread.java:3776)
      at android.app.ActivityThread.-wrap16(ActivityThread.java)
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1412)
      at android.os.Handler.dispatchMessage(Handler.java:102)
      at android.os.Looper.loop(Looper.java:148)
      at android.app.ActivityThread.main(ActivityThread.java:5461)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 
    'java.lang.String android.content.Intent.toString()' on a null object reference
      at android.app.Instrumentation.execStartActivity(Instrumentation.java:1485)
      at android.app.Activity.startActivityForResult(Activity.java:3930)
      at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:48)
      at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:75)
      at android.app.Activity.startActivityForResult(Activity.java:3890)
      at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:856)
      at android.app.Activity.startActivity(Activity.java:4213)
      at android.app.Activity.startActivity(Activity.java:4181)
      at com.devpost.airway.activities.MainActivity.onRequestPermissionsResult(MainActivity.java:140)
      at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582)
      at android.app.Activity.dispatchActivityResult(Activity.java:6460)
      at android.app.ActivityThread.deliverResults(ActivityThread.java:3729)
      at android.app.ActivityThread.handleSendResult(ActivityThread.java:3776) 
      at android.app.ActivityThread.-wrap16(ActivityThread.java) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1412) 
      at android.os.Handler.dispatchMessage(Handler.java:102) 
      at android.os.Looper.loop(Looper.java:148) 
      at android.app.ActivityThread.main(ActivityThread.java:5461) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

可能是什么原因造成的?

添加新方法

public static boolean hasPermissions(Context context, String... permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
    for (String permission : permissions) {
        if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
}
return true;
}

在全局中添加这个

int PERMISSION_ALL = 1; 
String[] PERMISSIONS = {Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE};

并在 onCreate

中写入以下代码
if(!hasPermissions(this, PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
}

堆栈跟踪似乎表明您的权限流工作正常,但从 onRequestPermissionsResult() 调用 startActivity 时崩溃了。您传递给 startActivityIntent 是否设置正确?我看不到它在那部分代码中设置。

另请注意,ContextCompat.checkSelfPermission 会代表您处理 SDK 版本检查,因此您应该能够使用

if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},REQUEST_PHONE_CALL);
}
else
{
    startActivity(intent);
}

单独使用,没有包装 SDK 版本检查代码。

将您的 onRequestPermissionsResult 更改为以下内容,您基本上需要先创建 intent ,然后在获得许可后调用它。这就是你做错的地方。

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_PHONE_CALL : {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));

                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                    startActivity(intent);
                }
            }
        }
    }
}

您需要在 onRequestPermissionsResult

中创建您的 Intent

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_PHONE_CALL: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));
                startActivity(intent);
            }
            else
            {

            }
            return;
        }
    }
} 

我建议在构建 Intent to call a particular number 时使用 ACTION_DIAL 而不是 ACTION_CALL。使用 ACTION_DIAL ,您的应用程序中不需要呼叫权限,因为 ACTION_DIAL 使用已输入的号码打开拨号器,并进一步允许用户决定是实际拨打电话还是修改 phone 呼叫前的号码或根本不呼叫。

Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + "Your Phone_number"));// Initiates the Intent 
startActivity(intent);
 private void makePhoneCall() {
    String number = items;
    if (number.trim().length() > 0) {
        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL);
        } else {
            String dial = "tel:" + number;
            startActivity(new Intent(Intent.ACTION_CALL, Uri.parse(dial)));
        }
    } else {
        Toast.makeText(MainActivity.this, "Enter Phone Number", Toast.LENGTH_SHORT).show();
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CALL) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            makePhoneCall();
        } else {
            Toast.makeText(this, "Permission DENIED", Toast.LENGTH_SHORT).show();
        }
    }
}

最好不要对该操作使用 phone_call 权限。 Google Play 控制台会提醒您,您应该避免向用户请求不必要的许可。这是一个更好的方法:

/**
 * Make a phone call. Send to the phone app
 *
 * @param phoneNumber the phone number to call
 */
private fun performPhoneCall(phoneNumber: String) {
    val intent = Intent(Intent.ACTION_DIAL)
    intent.data = Uri.parse("tel:$phoneNumber")
    try {
        context.startActivity(intent)
    } catch (ex: Exception) {
        displayErrorMessage()
    }
}