本記事について
この記事は CoreBluetoothForUnity Advent Calendar 2023 の20日目の記事です。
ネイティブプラグインからのコールバックは SynchronizationContext.Postを使って処理を遅延実行させるのがおすすめという話をします。
環境
- CoreBluetoothForUnity 0.4.6
Swift のネイティブプラグインはどのスレッドで呼ばれるのか
Swift のメインスレッドから呼ばれるコールバックは Unity のメインスレッドで実行されていました。
var context = SynchronizationContext.Current;
bool isUnityMainThread = context != null;
UnityEngine.Debug.Log($"MainThreadId: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
// => 1
UnityEngine.Debug.Log($"isUnityMainThread: {isUnityMainThread}");
// => true
Swift がメインスレッド以外から読んだ時にどうなるかは調べていません!
メインスレッドで呼ばれるから問題ないというわけではありません。
コールバックの処理をそのまま実行することの問題
エラーが投げられると Unity がクラッシュします。
クラッシュする理由は把握できていません。
対応
コールバックの処理をそのまま実行するのではなく、遅延実行させることでクラッシュを回避できます。
public class CBCentralManager : CBManager, INativeCentralManagerDelegate, IDisposable
{
...
/// <summary>
/// ICBCentralManagerDelegate callbacks will be called in this context.
/// </summary>
public SynchronizationContext CallbackContext { get; set; }
...
public CBCentralManager(ICBCentralManagerDelegate centralDelegate = null, CBCentralManagerInitOptions options = null)
{
...
CallbackContext = SynchronizationContext.Current;
}
...
// コールバックで呼ばれるメソッド
void INativeCentralManagerDelegate.DidConnect(string peripheralId)
{
CallbackContext.Post(_ =>
{
if (_disposed) return;
// 処理
}, null);
}
本対応の問題点
メインスレッドから SynchronizationContext.Post で同じくメインスレッドで実行するように指定すると、
実行が1フレーム遅れます。
CoreBluetoothForUnity ではこの1フレームの遅れは許容しました。
参考
おわりに
開発終盤まで何度もクラッシュしてたけれど、これで回避できましたという内容でした!