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
相关产品推荐
相关产品推荐

