异步事件处理程序有时会挂在 Xamarin.Forms

Posted

技术标签:

【中文标题】异步事件处理程序有时会挂在 Xamarin.Forms【英文标题】:async event handler sometimes hangs in Xamarin.Forms 【发布时间】:2018-12-18 17:42:54 【问题描述】:

我有一个异步事件处理程序,只要文本更改,就会在我的输入字段中触发。这是一个 Xamarin.Forms 应用程序,我正在使用事件处理程序 TextChanged 来模拟我的条目中的“过度类型”功能,例如在插入键处于活动状态时编写文本。但是,这有时会导致应用程序在 await Task.Yield() 调用之后死锁。需要此调用才能获得正确的行为,否则在执行调用后插入符号不会进入正确的位置。我不知道为什么代码手以及如何解决这个问题,所以任何帮助/建议将不胜感激。我猜这个问题与我的异步编程知识不足有关。

private static async void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)

        var guid = Guid.NewGuid();
        Debug.WriteLine($"DateTime.Now.ToString(): START guid.ToString()");
        var entry = sender as CustomEntry;
        if (entry.IsOvertypeRunning)
        
            return;
        
        else
        
            entry.IsOvertypeRunning = true;
        
        try
        
            entry.TextChanged -= OnOvertypeTextChanged;
            //Update value as in overtype mode
            var oldVal = e.OldTextValue;
            var newVal = e.NewTextValue;
            int? carret = null;
            //If new string is longer, we should go into overtype mode, otherwise ignore it
            if (newVal?.Length > oldVal?.Length)
            
                var i = entry.CursorPosition;
                var overtyped = oldVal.Substring(0, i) + newVal[i] + ((oldVal.Length > (i + 1)) ? oldVal.Substring(i + 1) : "");
                entry.Text = overtyped;
                carret = i + 1;
                //Check if new overtyped value is longer than max allowed size
                if (entry.OvertypeLength != -1 && entry.Text.Length > entry.OvertypeLength)
                
                    var cut = entry.Text.Substring(0, entry.OvertypeLength);
                    //If new text would be longer, then cut it
                    entry.Text = cut;
                    if (carret >= cut.Length)
                    
                        carret = cut.Length;
                    
                
            
            //For some reason, this is needed...
            if (carret != null)
            
                Debug.WriteLine($"DateTime.Now.ToString(): WAITING guid.ToString()");
                await Task.Yield();
                Debug.WriteLine($"DateTime.Now.ToString(): DONE_WAITING guid.ToString()");
                Debug.WriteLine($"DateTime.Now.ToString(): SETTING_CURSOR guid.ToString() @ carret.Value.ToString()");
                entry.CursorPosition = carret.Value;
                Debug.WriteLine($"DateTime.Now.ToString(): CURSOR_SET guid.ToString()");
            
            Debug.WriteLine($"DateTime.Now.ToString(): DONE guid.ToString()");
        
        catch (Exception ex)
        
            Debug.WriteLine($"DateTime.Now.ToString(): ERROR guid.ToString()", ex);
        
        finally
        
            entry.IsOvertypeRunning = false;
            entry.TextChanged += OnOvertypeTextChanged;
            Debug.WriteLine($"DateTime.Now.ToString(): END guid.ToString()");
        

所以会发生的是一切都会正常工作,只是在Entry上几个成功的焦点/取消焦点事件之后,它就会挂起。

所以日志记录会产生如下内容:

[0:] 18. 12. 2018 17:57:26: START bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: WAITING bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: DONE_WAITING bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: SETTING_CURSOR bba4b754-8f22-4a14-ae28-8536d3a1e3d1 @ 14
[0:] 18. 12. 2018 17:57:26: CURSOR_SET bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: DONE bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: END bba4b754-8f22-4a14-ae28-8536d3a1e3d1
[0:] 18. 12. 2018 17:57:26: START 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:26: WAITING 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:26: DONE_WAITING 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:26: SETTING_CURSOR 71bf9809-6217-4d73-94df-817042a251b9 @ 14
[0:] 18. 12. 2018 17:57:26: CURSOR_SET 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:26: DONE 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:26: END 71bf9809-6217-4d73-94df-817042a251b9
[0:] 18. 12. 2018 17:57:31: START f9fca969-e141-4a07-968d-e32616323995
[0:] 18. 12. 2018 17:57:31: WAITING f9fca969-e141-4a07-968d-e32616323995

然后该应用程序变得无响应并显示消息“应用程序 X 没有响应...”

在调用 CLICK 中描述的 HttpClient 服务时,我也发生了同样的异步“死锁”

我猜问题可能类似?

【问题讨论】:

使用 Device.BeginInvokeOnMainThread(async()=>);把代码放在这个 事件处理程序中的整个代码还是? @AditKothari 是的,在事件处理程序中你可以写这个 @DenisVitez 为什么使用Task.Yield() 这似乎是个问题?你的方法中没有其他东西是异步的,所以为什么不同步运行它。 Task.Yield() 不是用来保持 UI 响应的。 @JSteward 我正在使用 Task.Yield() 来获得正确的行为(因此光标确实位于正确的位置)。但是我发现没有必要使用它,所以我调整了我的代码以摆脱它并且它不再锁定。但是我仍然不明白为什么首先会发生锁定:) 【参考方案1】:

就像@JSteward 善意地指出的那样,Task.Yield() 的使用不是必需的,因为它是应用程序挂起的原因,现在已经解决了。可以在此处找到 OnTextChanged 处理程序的更新代码,它模拟改写模式以及如何轻松地将其与 Entry 类的 C# 扩展一起使用: https://forums.xamarin.com/discussion/143533/is-it-possible-to-have-overtype-functionality-in-entry

我希望这对将来的某人有所帮助:)

【讨论】:

以上是关于异步事件处理程序有时会挂在 Xamarin.Forms的主要内容,如果未能解决你的问题,请参考以下文章

SerialPort 类偶尔会挂在 Dispose 上

如何使事件处理程序异步运行?

为啥 MySQL 会挂在这个简单的子查询上?

Atmel Studio Dummy_Handler

当客户端连接不稳定时,在 heroku 上运行的 django 中的工作人员会挂在帖子上

为啥打开 mkfifo 管道时我的程序会挂起?