本文記錄一個 UNO 已知問題,在 UNO 裡面可以利用 SKXamlCanvas 對接 Skia 繪製到應用裡面。如果此時在後臺執行緒裡面呼叫 SKXamlCanvas 的 Invalidate 觸發介面的重新重新整理,但在具體的執行繪製 PaintSurface 事件裡面對外丟擲異常,將會導致應用炸掉
背景: 我準備在 UNO 裡面將 Microsoft.Maui.Graphics 對接進入,於是就用到了 SKXamlCanvas 控制元件。詳細請看 https://github.com/unoplatform/uno/discussions/15097
當前行為: 當我使用 SKXamlCanvas 時,如果我在 PaintSurface 事件裡面丟擲任何異常,且當前的 PaintSurface 事件是由後臺執行緒觸發的,那將導致我的程序崩潰
預期行為:即使在 PaintSurface 事件裡面丟擲任何異常,應用程式也可以正常工作且收集到異常,比如透過 TaskScheduler.UnobservedTaskException 事件收集到異常
復現步驟:
- 新增 SKXamlCanvas 到 xaml 裡
- 訂閱 SKXamlCanvas 的 PaintSurface 事件,且在事件實現方法丟擲異常
- 在後臺執行緒呼叫 SKXamlCanvas 的 Invalidate 方法
核心的程式碼實現如下
在 XAML 新增 SKXamlCanvas 控制元件
xmlns:sk="using:SkiaSharp.Views.Windows"
<sk:SKXamlCanvas x:Name="Canvas" PaintSurface="OnPaintSurface" />
在後臺程式碼裡面使用後臺執行緒呼叫 SKXamlCanvas 的 Invalidate 方法,且在 OnPaintSurface 丟擲異常
Task.Run(() =>
{
Canvas.Invalidate();
});
private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
throw new Exception();
}
本文以上的復現程式碼放在github 和 gitee 歡迎訪問
可以透過如下方式獲取以上的復現程式碼,先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin dde76effc23ebb9ee974b6ec276b242c39a50bdf
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin dde76effc23ebb9ee974b6ec276b242c39a50bdf
獲取程式碼之後,進入 JagobawearjiNeewhiqakerki 資料夾
報告地址: https://github.com/unoplatform/uno/issues/15123
原因:
這是由於在 SkiaSharp 裡面的錯誤實現導致踩到 dotnet 的另一個已知問題導致的。在 SKXamlCanvas 的具體實現裡面,透過 async void 等待執行結果,而根據 dotnet 的已知問題可以知道,在 async void 收到任何異常都會導致程序崩潰,此行為詳細請參閱 dotnet 警惕 async void 執行緒頂層異常
解決方法:
此問題已經被我修復,詳細請看 Avoid async void in SKXamlCanvas. by lindexi · Pull Request #2720 · mono/SkiaSharp