7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UnityにおけるAndroidJavaProxyの罠

Posted at

UnityからAndroid Javaをコールする場合、AndroidJavaClass, AndroidJavaObject, AndroidJavaProxyなどを使うと簡単にJavaを呼べます。これらを使うと、Javaを書かなくても簡単にC#から大体のAPIが叩けるように優れものです。特にAndroidJavaProxyは、JavaにあるinterfaceをC#側で実装できるというとんでもなく便利な代物です。

しかしながら先日このAndroidJavaProxyを使っていて少しハマったので、それを共有したいと思います。


罠の例

まず以下のJavaのinterfaceがあるとします。

public interface IHoge {
    void onHoge(int a, String b, String c);
}

これを実装したクラスをJava側に渡すための、UnityにおけるAndroidJavaProxyを継承したクラスは、ドキュメント読む限り以下のようになります。

class Hoge : AndroidJavaProxy
{
  public Hoge() : base("some.package.IHoge") {}
  void onHoge(int a, string b, string c)
  {
    // do something
  }
}

そしてこのクラスをインスタンス化してJava側に渡し、Javaから以下のように呼び出したとします・・・

IHoge hoge = getHoge();
hoge.onHoge(0, "some string", null);

結果

Unity > No such proxy method: onHoge(int, string AndroidJavaObject)

!?
何で最後の型がAndroidJavaObjectになってるんだ・・・

コードダイブ

原因を探るために、コードダイブしてみましょう。
もちろんUnityはコード非公開ですが、C#のdllなら簡単にデコンパイルできます。
GitHub
若干古いですが、有志がデコンパイルしたものをGitHubにあげてたりもしますので、こちらを見てみることにします・・・

原因

原因はAndroidJavaProxyの実装から読み取れます。

		public virtual AndroidJavaObject Invoke(string methodName, object[] args)
		{
			Exception ex = null;
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			Type[] array = new Type[args.Length];
			for (int i = 0; i < args.Length; i++)
			{
				array[i] = ((args[i] != null) ? args[i].GetType() : typeof(AndroidJavaObject));
			}
			try
			{
				MethodInfo method = base.GetType().GetMethod(methodName, bindingAttr, null, array, null);
				if (method != null)
				{
					AndroidJavaObject result = _AndroidJNIHelper.Box(method.Invoke(this, args));
					return result;
				}
			}
			(中略)
	    }

Javaから呼び出された時に、AndroidJavaProxyは対応するメソッドをリフレクションを使って呼び出すようになっていますが、AndroidJavaObject型をC#の基本型に変換できる場合は変換してから探します。しかしながらnullだけは型がわからないため、AndroidJavaObjectで探しているために、エラーが出てしまっていたのです。
自動変換される型はプリミティブ型と文字列型だけですが、文字列型だけnullableなのでこのようなことが起こってしまいます。

解決方法

ぶっちゃけリフレクションとか重いので、以下のようにすればパフォーマンス的にも良く、上のような問題は起こりません。

class Hoge : AndroidJavaProxy
{
  public Hoge() : base("some.package.IHoge") {}
  public override AndroidJavaObject Invoke(string methodName, object[] args)
  {
    onHoge((int)args[0], (string)args[1], (string)args[2]);
    return null;
  }
  void onHoge(int a, string b, string c)
  {
    // do something
  }
}

もちろんこれは1メソッドの場合なので、複数メソッドの場合は工夫が必要です。またエラー処理もやっていません。適宜調整して下さい。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?