killapp/Assets/Scripts/Video/MainThreadDispatcher.cs

67 lines
2.2 KiB
C#
Raw Normal View History

using System;
using System.Collections.Concurrent;
using System.Threading;
using UnityEngine;
namespace Kill.Video
{
/// <summary>
/// 把任意 Action 排到 Unity 主线程执行。
/// 背景:IL2CPP 的自建后台线程 JVM attach 不可靠,AndroidJavaClass/AndroidJNI
/// 在这些线程上 FindClass 会偶发返回空,导致"类未找到/偶尔成功偶尔失败"。
/// 用它在主线程上跑所有 JNI 调用即可稳定。
/// </summary>
public static class MainThreadDispatcher
{
private static readonly ConcurrentQueue<Action> _pending = new ConcurrentQueue<Action>();
private static int _mainThreadId = -1;
private static bool _ensured;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void Ensure()
{
if (_ensured) return;
_mainThreadId = Thread.CurrentThread.ManagedThreadId;
var go = new GameObject("MainThreadDispatcher");
go.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(go);
go.AddComponent<DispatcherBehaviour>();
_ensured = true;
}
public static bool IsMainThread =>
!_ensured || Thread.CurrentThread.ManagedThreadId == _mainThreadId;
/// <summary>在主线程执行 action;若调用方已是主线程则直接内联执行。</summary>
public static void RunOnMainThread(Action action)
{
if (action == null) return;
if (IsMainThread)
{
action();
return;
}
using (var done = new ManualResetEventSlim(false))
{
_pending.Enqueue(() =>
{
try { action(); }
finally { done.Set(); }
});
done.Wait();
}
}
private class DispatcherBehaviour : MonoBehaviour
{
private void Update()
{
while (_pending.TryDequeue(out var a))
{
try { a(); }
catch (Exception e) { Debug.LogException(e); }
}
}
}
}
}