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

如何在Xamarin.Forms中通过DependencyService实现无拨号界面直接通话?

解决Xamarin.Forms Android端直接通话的"Unable to add window - token null"异常

看起来你碰到的问题核心是Android上下文使用不当,加上可能缺少必要的权限校验。咱们一步步来解决:

1. 先搞定通话权限(关键前提)

Android 6.0(API 23)及以上需要动态申请CALL_PHONE权限,光在Manifest里加还不够。

  • 首先在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.CALL_PHONE" />
  • 然后在调用通话前,先检查并申请权限:
    在Android端的实现类中加入权限校验逻辑:
// 在MakeQuickCall方法开头添加
if (ContextCompat.CheckSelfPermission(MainActivity.Instance, Manifest.Permission.CallPhone) != Permission.Granted)
{
    ActivityCompat.RequestPermissions(MainActivity.Instance, new[] { Manifest.Permission.CallPhone }, 100);
    return;
}

2. 修复上下文问题,避免token null异常

你用MainActivity静态实例的思路是对的,但要确保实例在生命周期中正确维护,并且必须用Activity上下文启动Intent,不能用Application上下文。

修正MainActivity的静态实例维护

public class MainActivity : FormsAppCompatActivity
{
    internal static MainActivity Instance { get; private set; }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Instance = this; // 初始化实例
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
    }

    // 销毁时置空实例,避免内存泄漏
    protected override void OnDestroy()
    {
        base.OnDestroy();
        Instance = null;
    }

    // 处理权限申请结果
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

修正Android端的通话实现类

[assembly: Dependency(typeof(PhoneCall_Droid))]
namespace MakeCall.Droid
{
    public class PhoneCall_Droid : IPhoneCall
    {
        public void MakeQuickCall(string PhoneNumber)
        {
            try
            {
                var activity = MainActivity.Instance;
                if (activity == null)
                {
                    throw new Exception("当前没有可用的Activity上下文");
                }

                // 检查权限
                if (ContextCompat.CheckSelfPermission(activity, Manifest.Permission.CallPhone) != Permission.Granted)
                {
                    ActivityCompat.RequestPermissions(activity, new[] { Manifest.Permission.CallPhone }, 100);
                    return;
                }

                // 构建通话Intent并启动
                var uri = Android.Net.Uri.Parse($"tel:{PhoneNumber}");
                var intent = new Intent(Intent.ActionCall, uri);
                intent.AddFlags(ActivityFlags.NewTask); // 确保Intent在当前栈中启动
                activity.StartActivity(intent);
            }
            catch (Exception ex)
            {
                // 用Activity上下文弹窗,避免token null问题
                new AlertDialog.Builder(MainActivity.Instance)
                    .SetPositiveButton("OK", (sender, args) => { })
                    .SetMessage(ex.ToString())
                    .SetTitle("Android Exception")
                    .Show();
            }
        }
    }
}

3. 为什么之前的方案不行?

  • Forms.Context在新版本Xamarin.Forms中已被标记为过时,它返回的是Application上下文,而Application上下文无法启动需要依附Activity的Intent(这就是token null异常的根源)。
  • 你之前用Application上下文弹AlertDialog也会触发同样的异常,因为弹窗必须依附于一个活跃的Activity。

额外提示:用Xamarin.Essentials简化实现

其实Xamarin.Essentials已经内置了通话功能,如果你不需要完全跳过拨号界面,可以直接用它来减少代码量:

// 共享库中直接调用
try
{
    if (Xamarin.Essentials.PhoneDialer.IsSupported)
    {
        await Xamarin.Essentials.PhoneDialer.OpenAsync("+xxxxxxxxx");
    }
}
catch (Exception ex)
{
    await DisplayAlert("Alert!!!", ex.Message, "ok");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:23:55