Open7

UniRx/UniTask/SynchronizationContextでメインスレッドに切替

Mitsuhiro KogaMitsuhiro Koga

Unity Androidでネイティブに渡したコールバックをUniRxを使ってメインスレッドで実行する方法。AddToは内部でメインスレッドが必要なGetComponentを使っているので代わりにCompositeDisposableで掃除する。

NativeLibrary.kt
package com.example

interface Callback {
    fun invoke()
}

object NativeLibrary {
    @JvmStatic
    fun call(action: Callback) {
        // ネイティブ側でC#のメソッドをコールバックしたい
        action.invoke()
    }
}
Callback.cs
using System;
using UnityEngine;
using UniRx;

public class Callback : AndroidJavaProxy, IDisposable
{
    private Action callback;
    private Disposable disposable;

    public Callback(Action action) : base("com.example.Callback")
    {
        callback = action;
    }

    // invokeメソッドを実装
    public void invoke()
    {
        // callbackをメインスレッドで実行する
        disposable = Observable.ReturnUnit()
            .ObserveOnMainThread()
            .Subscribe(_ =>
            {
                callback?.Invoke();
            });

    }

    public void Dispose()
    {
        disposable?.Dispose();
        callback = null;
    }
}
NativePlugin.cs
using System;
using UnityEngine;
using UniRx;

public class NativePlugin : MonoBehaviour
{
    private CompositeDisposable compositeDisposable;
    private AndroidJavaClass nativeLibrary;

    private void Awake()
    {
        compositeDisposable = new CompositeDisposable();
        nativeLibrary = new AndroidJavaClass("com.example.NativeLibrary");
    }

    public void Call()
    {
        var callback = new Callback(() => Debug.Log("call"));
        nativeLibrary.CallStatic("call", callback);
        compositeDisposable.Add(callback);
    }

    void OnDestroy()
    {
        compositeDisposable.Clear();
        nativeLibrary.Dispose();
    }
}
Mitsuhiro KogaMitsuhiro Koga

SynchronizationContext[1]を使う場合

Callback.cs
using System;
using System.Threading;
using UnityEngine;

public class Callback : AndroidJavaProxy
{
    private SynchronizationContext context;
    private Action callback;

    public Callback(SynchronizationContext context, Action callback) : base("com.example.Callback")
    {
        this.context = context;
        this.callback = callback;
    }

    public void invoke()
    {
        context.Post(_ => callback?.Invoke(), null);
    }
}
NativePlugin.cs
using System;
using System.Threading;
using UnityEngine;

public class NativePlugin : MonoBehaviour
{
    private SynchronizationContext context;
    private AndroidJavaClass nativeLibrary;

    private void Awake()
    {
        context = SynchronizationContext.Current;
        nativeLibrary = new AndroidJavaClass("com.example.NativeLibrary");
    }

    public void Call()
    {
        var callback = new Callback(context, () => Debug.Log("call"));
        nativeLibrary.CallStatic("call", callback);
    }
}
脚注
  1. 【Unity開発者向け】「SynchronizationContext」と「Taskのawait」 ↩︎

Mitsuhiro KogaMitsuhiro Koga

UniTask.Yield[1]を使う場合

Callback.cs
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Callback : AndroidJavaProxy
{
    private CancellationToken token;
    private Action callback;
    
    public Callback(CancellationToken token, Action callback) : base("com.example.Callback")
    {
        this.token = token;
        this.callback = callback;
    }

    public void invoke()
    {
        _ = AsyncInvoke();
    }

    private async UniTaskVoid AsyncInvoke()
    {
        await UniTask.Yield(token);
        callback?.Invoke();
    }
}
NativeLibrary.cs
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class NativePlugin : MonoBehaviour
{
    private CancellationToken token;
    private AndroidJavaClass nativeLibrary;

    private void Awake()
    {
        token = this.GetCancellationTokenOnDestroy();
        nativeLibrary = new AndroidJavaClass("com.example.NativeLibrary");
    }

    public void Call()
    {
        var callback = new Callback(token, () => Debug.Log("call"));
        nativeLibrary.CallStatic("call", callback);
    }
}
脚注
  1. https://twitter.com/naninuneno_y/status/1367393724947259393 ↩︎

Mitsuhiro KogaMitsuhiro Koga

UniTask.Post[1]を使う場合

Callback.cs
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class Callback : AndroidJavaProxy
{
    private Action callback;

    public Callback(Action callback) : base("com.example.Callback")
    {
        this.callback = callback;
    }

    public void invoke()
    {
        UniTask.Post(callback);
    }
}
NativeLibrary.cs
using System;
using UnityEngine;

public class NativePlugin : MonoBehaviour
{
    private AndroidJavaClass nativeLibrary;

    private void Awake()
    {
        nativeLibrary = new AndroidJavaClass("com.example.NativeLibrary");
    }

    public void Call()
    {
        var callback = new Callback(() => Debug.Log("call"));
        nativeLibrary.CallStatic("call", callback);
    }
}
脚注
  1. https://twitter.com/toRisouP/status/1367406192847249409 ↩︎