Open7
UniRx/UniTask/SynchronizationContextでメインスレッドに切替
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();
}
}
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);
}
}
UniRxのObservable.Returnでメインスレッドを指定する場合[1]
これは元の書き方より1行減らせます。
disposable = Observable.Return(Unit.Default, Scheduler.MainThread)
.Subscribe(_ =>
{
callback?.Invoke();
});
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);
}
}
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);
}
}
UniTask.Run
はThreadPool上で動くのでメインスレッド切り替えが目的だと推奨されない。
昔から使われているUnityMainThreadDispatcher[1]でも実現できそう。