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

在Xamarin.Forms中调用原生通话与聊天API的技术问询

嘿,我之前在几个Xamarin.Forms项目里处理过类似的原生功能集成问题,尤其是语音通话、聊天和图片编辑器这类需要和原生SDK深度交互的功能,给你梳理几个可行的方案和代码示例,应该能帮你突破瓶颈:

一、语音通话与聊天功能的原生集成方案

因为Sinch、Twilio没有官方Xamarin.Forms包,我们可以用DependencyService(跨平台调用原生API)或者自定义渲染器(嵌入原生UI控件)来实现,先从DependencyService的方案说起:

1. 用DependencyService对接Sinch SDK

首先在共享项目定义统一接口,然后分别在Android和iOS项目实现:

共享项目接口定义

public interface ISinchCommunicationService
{
    // 初始化Sinch客户端
    Task InitializeAsync(string appKey, string appSecret, string userId);
    // 发起语音通话
    void StartVoiceCall(string targetUserId);
    // 发送文本消息
    void SendTextMessage(string targetUserId, string content);
    // 消息接收事件
    event EventHandler<MessageReceivedArgs> OnMessageReceived;
}

// 消息接收事件参数
public class MessageReceivedArgs : EventArgs
{
    public string SenderId { get; set; }
    public string MessageContent { get; set; }
}

Android端实现(绑定Sinch AAR)

先解决你之前绑定失败的问题:把Sinch的AAR文件放到Android项目的Libs文件夹,右键设置属性为AndroidLibrary,如果有绑定冲突,在绑定项目的Metadata.xml里调整(比如重命名混淆类、移除冲突方法):

[assembly: Dependency(typeof(SinchCommunicationAndroid))]
namespace YourApp.Droid.Services
{
    public class SinchCommunicationAndroid : ISinchCommunicationService
    {
        private IClient _sinchClient;
        public event EventHandler<MessageReceivedArgs> OnMessageReceived;

        public async Task InitializeAsync(string appKey, string appSecret, string userId)
        {
            _sinchClient = Sinch.Client.Create(appKey, appSecret, userId, Android.App.Application.Context);
            _sinchClient.SetSupportCalling(true);
            _sinchClient.SetSupportMessaging(true);
            // 注册消息监听
            _sinchClient.MessagingClient.AddMessageReceivedListener(new MessageListener(this));
            await Task.Run(() => _sinchClient.Start());
        }

        public void StartVoiceCall(string targetUserId)
        {
            if (_sinchClient.IsStarted)
            {
                _sinchClient.CallClient.CallUser(targetUserId);
            }
        }

        public void SendTextMessage(string targetUserId, string content)
        {
            var message = _sinchClient.MessagingClient.CreateMessage(targetUserId, content);
            _sinchClient.MessagingClient.Send(message);
        }

        // 消息监听内部类
        private class MessageListener : Java.Lang.Object, IMessageReceivedListener
        {
            private readonly SinchCommunicationAndroid _parent;
            public MessageListener(SinchCommunicationAndroid parent) => _parent = parent;

            public void OnMessageReceived(IMessage message)
            {
                _parent.OnMessageReceived?.Invoke(this, new MessageReceivedArgs
                {
                    SenderId = message.SenderId,
                    MessageContent = message.Text
                });
            }
        }
    }
}

iOS端实现(用CocoaPods导入Sinch SDK)

先在iOS项目的Podfile里添加pod 'SinchSDK',然后执行pod install,接着实现接口:

[assembly: Dependency(typeof(SinchCommunicationiOS))]
namespace YourApp.iOS.Services
{
    public class SinchCommunicationiOS : ISinchCommunicationService
    {
        private SINClient _sinchClient;
        public event EventHandler<MessageReceivedArgs> OnMessageReceived;

        public async Task InitializeAsync(string appKey, string appSecret, string userId)
        {
            _sinchClient = Sinch.SINClient.ClientWithApplicationKey(appKey, applicationSecret: appSecret, userId: userId, environmentHost: "clientapi.sinch.com");
            _sinchClient.CallDelegate = new CallDelegate();
            _sinchClient.MessagingDelegate = new MessagingDelegate(this);
            _sinchClient.SetSupportCalling(true);
            _sinchClient.SetSupportMessaging(true);
            await Task.Run(() => _sinchClient.Start());
        }

        public void StartVoiceCall(string targetUserId)
        {
            if (_sinchClient.IsStarted)
            {
                _sinchClient.CallClient.CallUserWithId(targetUserId);
            }
        }

        public void SendTextMessage(string targetUserId, string content)
        {
            var message = _sinchClient.MessagingClient.CreateMessageWithRecipientId(targetUserId, content);
            _sinchClient.MessagingClient.SendMessage(message);
        }

        // 消息委托内部类
        private class MessagingDelegate : NSObject, ISINMessagingDelegate
        {
            private readonly SinchCommunicationiOS _parent;
            public MessagingDelegate(SinchCommunicationiOS parent) => _parent = parent;

            public void DidReceiveMessage(ISINMessage message)
            {
                _parent.OnMessageReceived?.Invoke(this, new MessageReceivedArgs
                {
                    SenderId = message.SenderId,
                    MessageContent = message.Text
                });
            }
        }

        // 通话委托内部类
        private class CallDelegate : NSObject, ISINCallDelegate
        {
            public void DidEstablishCall(ISINCall call)
            {
                // 处理通话建立逻辑
            }

            public void DidEndCall(ISINCall call)
            {
                // 处理通话结束逻辑
            }
        }
    }
}

2. 嵌入原生通话UI控件(自定义渲染器)

如果需要显示原生的通话界面(比如Sinch的通话控制栏),可以用自定义渲染器在XF页面中嵌入原生控件:

// 共享项目定义空视图
public class SinchCallView : View { }

// Android端渲染器
[assembly: ExportRenderer(typeof(SinchCallView), typeof(SinchCallViewRenderer))]
namespace YourApp.Droid.Renderers
{
    public class SinchCallViewRenderer : ViewRenderer<SinchCallView, CallScreen>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<SinchCallView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
            {
                // 获取Sinch的通话界面控件
                var callScreen = new CallScreen(Context);
                SetNativeControl(callScreen);
            }
        }
    }
}
二、图片编辑器功能的原生实现

图片编辑器需要原生UI交互,用自定义渲染器最合适,这里以裁剪和滤镜功能为例:

1. 共享项目定义编辑器视图

public class ImageEditorView : View
{
    public static readonly BindableProperty SourceProperty = BindableProperty.Create(
        nameof(Source), typeof(ImageSource), typeof(ImageEditorView));

    public ImageSource Source
    {
        get => (ImageSource)GetValue(SourceProperty);
        set => SetValue(SourceProperty, value);
    }

    // 编辑完成事件
    public event EventHandler<byte[]> OnImageEdited;
}

2. Android端实现(用Android Image Cropper库)

先安装Xamarin.Android.ImageCropper NuGet包,然后写渲染器:

[assembly: ExportRenderer(typeof(ImageEditorView), typeof(ImageEditorViewRenderer))]
namespace YourApp.Droid.Renderers
{
    public class ImageEditorViewRenderer : ViewRenderer<ImageEditorView, ImageView>
    {
        private ImageView _imageView;
        private readonly Context _context;

        public ImageEditorViewRenderer(Context context) : base(context) => _context = context;

        protected override void OnElementChanged(ElementChangedEventArgs<ImageEditorView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
            {
                _imageView = new ImageView(_context)
                {
                    ScaleType = ImageView.ScaleType.FitCenter,
                    Clickable = true
                };
                // 点击触发编辑
                _imageView.Click += OnImageClick;
                SetNativeControl(_imageView);
            }
            if (e.NewElement != null)
            {
                LoadImage(e.NewElement.Source);
            }
        }

        private void LoadImage(ImageSource source)
        {
            if (source is FileImageSource fileSource)
            {
                Glide.With(_context).Load(fileSource.File).Into(_imageView);
            }
        }

        private void OnImageClick(object sender, EventArgs e)
        {
            var cropIntent = new CropImage.Intent(_imageView.Drawable.ToBitmap())
            {
                Guidelines = CropImageView.Guidelines.On,
                AspectRatioX = 1,
                AspectRatioY = 1
            };
            ((Activity)_context).StartActivityForResult(cropIntent, 1001);
        }

        // 在MainActivity的OnActivityResult中处理裁剪结果
        // 可以用MessagingCenter通知渲染器触发OnImageEdited事件
    }
}

3. iOS端实现(用TOCropViewController库)

安装Xamarin.iOS.TOCropViewController NuGet包,然后写渲染器:

[assembly: ExportRenderer(typeof(ImageEditorView), typeof(ImageEditorViewRenderer))]
namespace YourApp.iOS.Renderers
{
    public class ImageEditorViewRenderer : ViewRenderer<ImageEditorView, UIImageView>
    {
        private UIImageView _imageView;

        protected override void OnElementChanged(ElementChangedEventArgs<ImageEditorView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
            {
                _imageView = new UIImageView
                {
                    ContentMode = UIViewContentMode.ScaleAspectFit,
                    UserInteractionEnabled = true
                };
                var tapGesture = new UITapGestureRecognizer(OnImageTap);
                _imageView.AddGestureRecognizer(tapGesture);
                SetNativeControl(_imageView);
            }
            if (e.NewElement != null)
            {
                LoadImage(e.NewElement.Source);
            }
        }

        private void LoadImage(ImageSource source)
        {
            if (source is FileImageSource fileSource)
            {
                _imageView.Image = UIImage.FromFile(fileSource.File);
            }
        }

        private void OnImageTap(UITapGestureRecognizer sender)
        {
            var cropVC = new TOCropViewController(_imageView.Image)
            {
                Delegate = new CropDelegate(Element)
            };
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(cropVC, true, null);
        }

        private class CropDelegate : TOCropViewControllerDelegate
        {
            private readonly ImageEditorView _element;
            public CropDelegate(ImageEditorView element) => _element = element;

            public override void CropViewControllerDidCropToImage(TOCropViewController cropViewController, UIImage image, CGRect cropRect, nint angle)
            {
                var imageBytes = image.AsJPEG(0.8f).ToArray();
                _element.OnImageEdited?.Invoke(this, imageBytes);
                cropViewController.DismissViewController(true, null);
            }
        }
    }
}
三、绑定AAR库的常见坑点解决

你之前绑定Sinch AAR失败,大概率是以下问题:

  • 混淆类名冲突:在绑定项目的Metadata.xml里用<attr>重命名混淆类,比如:
    <attr path="/api/package[@name='com.sinch.android.rtc.internal']/class[@name='a']" name="managedName">SinchInternalClient</attr>
    
  • 依赖缺失:Sinch依赖Google Play Services,需要在Android项目安装对应的NuGet包(比如Xamarin.GooglePlayServices.Base)
  • 方法签名不匹配:用<remove-node>移除冲突的方法,或者<attr>修改参数类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:16:37