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

Xamarin.Forms应用多次发送FCM消息时崩溃求助

Xamarin.Forms消息应用二次点击发送崩溃问题分析

问题场景

使用Xamarin.Forms开发消息应用,发送按钮点击事件代码如下:

private async void send_Clicked(object sender, EventArgs e)
{
    await Task.Run(() => SendNotification(token, "title", message.Text));
}

SendNotification方法实现:

public string SendNotification(string DeviceToken, string title, string msg)
{
    var result = "-1";
    var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
    httpWebRequest.ContentType = "application/json";
    httpWebRequest.Headers.Add(string.Format("Authorization: key={0}", serverKey));
    httpWebRequest.Headers.Add(string.Format("Sender: id={0}", senderId));
    httpWebRequest.Method = "POST";

    var payload = new
    {
        //to= "/topics/klm",
        to = DeviceToken,
        //to = "egjLx6VdFS0:APA91bGQMSSRq_wCzywNC01zJi4FBtHXrXuL-p4vlkl3a3esdH8lxo7mQZUBlrTi7h-6JXx0GrJbwc9Vx6M5Q4OV_3CArcdlP0XMBybervQvfraWvqCgaa75gu9SVzjY4V_qd36JGg4A",
        priority = "high",
        content_available = true,

        data = new
        {
            text = msg,
        },
    };
    var serializer = new JsonSerializer();
    using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
    {
        string json = JsonConvert.SerializeObject(payload);
        streamWriter.Write(json);
        streamWriter.Flush();
    }

    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
    using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
    {
        result = streamReader.ReadToEnd();
    }
    return result;
}

其中private string webAddr = "https://fcm.googleapis.com/fcm/send";。首次点击发送正常接收消息,第二次点击时应用冻结后崩溃,提示关闭或等待。

核心原因

  • 线程池阻塞:Task.Run将同步的SendNotification方法放到线程池执行,而HttpWebRequest的GetRequestStream()和GetResponse()都是同步阻塞调用,会长时间占用线程池线程。第二次点击时,线程池无可用线程处理新请求,导致UI线程等待,最终应用假死崩溃。
  • 未处理异常:同步网络请求若出现超时、连接失败等情况,会抛出未捕获异常,直接引发崩溃。

解决办法

1. 替换为异步网络API

使用HttpClient的异步方法,避免阻塞线程池线程:

private async void send_Clicked(object sender, EventArgs e)
{
    await SendNotificationAsync(token, "title", message.Text);
}

public async Task<string> SendNotificationAsync(string DeviceToken, string title, string msg)
{
    var result = "-1";
    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Add("Authorization", $"key={serverKey}");
        httpClient.DefaultRequestHeaders.Add("Sender", $"id={senderId}");
        
        var payload = new
        {
            to = DeviceToken,
            priority = "high",
            content_available = true,
            data = new { text = msg }
        };
        
        var json = JsonConvert.SerializeObject(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await httpClient.PostAsync(webAddr, content);
        if (response.IsSuccessStatusCode)
        {
            result = await response.Content.ReadAsStringAsync();
        }
    }
    return result;
}

2. 添加异常捕获

捕获网络异常,避免应用直接崩溃:

public async Task<string> SendNotificationAsync(string DeviceToken, string title, string msg)
{
    var result = "-1";
    try
    {
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Add("Authorization", $"key={serverKey}");
            httpClient.DefaultRequestHeaders.Add("Sender", $"id={senderId}");
            
            var payload = new
            {
                to = DeviceToken,
                priority = "high",
                content_available = true,
                data = new { text = msg }
            };
            
            var json = JsonConvert.SerializeObject(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            
            var response = await httpClient.PostAsync(webAddr, content);
            if (response.IsSuccessStatusCode)
            {
                result = await response.Content.ReadAsStringAsync();
            }
        }
    }
    catch (HttpRequestException ex)
    {
        // 可添加日志记录或用户提示
        result = $"请求失败: {ex.Message}";
    }
    return result;
}

3. 禁用重复点击

点击后暂时禁用按钮,防止用户重复触发请求:

private async void send_Clicked(object sender, EventArgs e)
{
    var sendBtn = sender as Button;
    sendBtn.IsEnabled = false;
    try
    {
        await SendNotificationAsync(token, "title", message.Text);
    }
    finally
    {
        sendBtn.IsEnabled = true;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 06:09:19