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

Xamarin Forms中WPF Dispatcher.PushFrame的等价实现方案咨询

在Xamarin.Forms中实现类似WPF的WaitWithPumping/ResultWithPumping扩展方法

针对你的遗留项目场景——Lua脚本引擎需要在UI线程同步调用异步方法,且第三方库强制要求UI线程执行任务,无法修改现有同步架构,这里提供适配iOS和Android平台的扩展方法,实现和WPF中DispatcherFrame相同的效果:在等待异步任务时手动泵消息循环,避免UI阻塞。

扩展方法实现

using System;
using System.Threading.Tasks;
using Xamarin.Forms;
#if __IOS__
using Foundation;
#endif
#if __ANDROID__
using Android.OS;
#endif

public static class TaskPumpingExtensions
{
    /// <summary>
    /// 在UI线程等待异步任务完成,同时泵消息循环避免UI阻塞
    /// </summary>
    public static void WaitWithPumping(this Task task)
    {
        if (task == null)
            throw new ArgumentNullException(nameof(task));

        // 任务已完成直接等待,跳过消息循环
        if (task.IsCompleted)
        {
            task.Wait();
            return;
        }

        bool isCompleted = false;
        // 在UI线程上下文标记任务完成
        task.ContinueWith(_ => isCompleted = true, TaskScheduler.FromCurrentSynchronizationContext());

        // 启动平台特定的消息循环泵
        PumpMessageLoopUntil(() => isCompleted);

        // 确保任务完成后处理异常
        task.Wait();
    }

    /// <summary>
    /// 在UI线程等待异步任务并获取结果,同时泵消息循环避免UI阻塞
    /// </summary>
    public static T ResultWithPumping<T>(this Task<T> task)
    {
        if (task == null)
            throw new ArgumentNullException(nameof(task));

        // 任务已完成直接返回结果
        if (task.IsCompleted)
            return task.Result;

        bool isCompleted = false;
        T result = default;
        // 在UI线程上下文获取结果并标记完成
        task.ContinueWith(t =>
        {
            result = t.Result;
            isCompleted = true;
        }, TaskScheduler.FromCurrentSynchronizationContext());

        // 启动平台特定的消息循环泵
        PumpMessageLoopUntil(() => isCompleted);

        return result;
    }

    private static void PumpMessageLoopUntil(Func<bool> exitCondition)
    {
        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                PumpIosMessageLoop(exitCondition);
                break;
            case Device.Android:
                PumpAndroidMessageLoop(exitCondition);
                break;
            default:
                throw new PlatformNotSupportedException($"当前平台{Device.RuntimePlatform}暂不支持该消息循环泵实现");
        }
    }

#if __IOS__
    private static void PumpIosMessageLoop(Func<bool> exitCondition)
    {
        var currentRunLoop = NSRunLoop.Current;
        var defaultMode = NSRunLoop.DefaultMode;

        // 持续处理消息直到任务完成,每次处理0.01秒平衡响应性和性能
        while (!exitCondition() && currentRunLoop.RunUntil(NSDate.FromTimeIntervalSinceNow(0.01)))
        {
        }
    }
#endif

#if __ANDROID__
    private static void PumpAndroidMessageLoop(Func<bool> exitCondition)
    {
        var looper = Looper.MyLooper();
        var messageQueue = looper.MessageQueue;

        while (!exitCondition())
        {
            // 取出并处理队列中的消息
            if (messageQueue.Next(out var message))
            {
                try
                {
                    looper.Target.DispatchMessage(message);
                }
                finally
                {
                    message.Recycle();
                }
            }
        }
    }
#endif
}

核心原理说明

  • 和WPF的DispatcherFrame思路一致:通过手动处理UI线程的消息队列,让UI在等待异步任务时仍能响应用户交互,不会出现卡顿。
  • 针对iOS和Android的消息循环机制分别实现:
    • iOS:利用NSRunLoop的RunUntil方法,每次处理一小段时间的消息后检查任务是否完成。
    • Android:直接操作Looper和MessageQueue,逐个处理队列中的消息直到任务完成。
  • 先判断任务是否已完成,避免不必要的消息循环开销,同时在任务完成后通过task.Wait()确保异常被正确抛出。

用法示例(和你WPF中的代码完全兼容)

public async void RunTaskOnUi1Async() { 
    await ProcessAsync(); 
    UpdateUiInner(); 
} 

public async object RunTaskOnUi2Async() { 
    var result = await ProcessAsync(); 
    UpdateUiInner(result); 
    return result; 
} 

// 在UI线程中调用(比如Lua引擎的回调)
public void RunTasks() { 
    UpdateUi(); 
    RunTaskOnUi1Async().WaitWithPumping(); 
    UpdateUi(); 
    var result = RunTaskOnUi2Async().ResultWithPumping(); 
    UpdateUi(result); 
}

注意事项

  1. 必须在UI线程调用:这些扩展方法依赖UI线程的消息循环,在后台线程调用会导致异常。
  2. 避免嵌套调用:不要在WaitWithPumping/ResultWithPumping内部再调用同类方法,可能导致消息循环死锁。
  3. 性能考量:长时间运行的异步任务仍可能影响UI响应,若有条件建议逐步优化异步架构,但针对遗留项目这个方案能满足需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 16:37:46