简体   繁体   中英

Unity: Call Android Kotlin Function Inside Companion Object from Unity

I am a native developer and new to Unity. I am trying to play to my strengths by coding in Kotlin and then using my code as a plugin in Unity. One issue I am running into is that I cannot figure out how to reference a function that is inside of a companion object.

Here is my Kotlin class

class MyAnalytics internal constructor(
    private val MyAnalyticsConfiguration: MyAnalyticsConfiguration
) : MyAnalyticsLogger {

    .
    .
    .

    override fun logEvent(eventRequest: Event?) {
        // some code in here
    }

    companion object {

        @JvmStatic
        fun factory(
            context: Context,
            MyAnalyticsConfiguration: MyAnalyticsConfiguration = MyAnalyticsConfiguration()
        ) : MyAnalytics {
            val eventExecutor = MyWorkManager(
                workManager = WorkManager.getInstance(context.applicationContext),
                MyAnalyticsConfiguration = MyAnalyticsConfiguration
            )
            return MyAnalytics(
                MyAnalyticsConfiguration
            )
        }
    }
}

I want to be able to access the factory function from my Unity code.

In Kotlin I can do so like this,

MyAnalytics.factory(
      context = requireActivity()
)

But how can I do the same in Unity? Here is what I have tried.


AndroidJavaClass UnityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject UnityPlayerActivity = UnityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject myAnalytics = new AndroidJavaObject("com.test.analytics.MyAnalytics");
AndroidJavaObject analyticsObject = myAnalytics.Call<AndroidJavaObject>("factory", UnityPlayerActivity); //Also tried with 'CallStatic'; no result, same error

The error I am receiving is,

AndroidJavaException: java.lang.NoSuchMethodError: no static method with name='factory' signature='(Landroid.app.Application;)Ljava/lang/Object;' in class Ljava.lang.Object; 12-10 14:00:21.883 13537 13674 E Unity: java.lang.NoSuchMethodError: no static method with name='factory' signature='(Landroid.app.Application;)Ljava/lang/Object;' in class Ljava.lang.Object; 12-10 14:00:21.883 13537 13674 E Unity: at com.unity3d.player.ReflectionHelper.getMethodID(Unknown Source:162) 12-10 14:00:21.883 13537 13674 E Unity: at com.unity3d.player.UnityPlayer.nativeRender(Native Method) 12-10 14:00:21.883 13537 13674 E Unity: at com.unity3d.player.UnityPlayer.access$300(Unknown Source:0) 12-10 14:00:21.883 13537 13674 E Unity: at com.unity3d.player.UnityPlayer$e$1.handleMessage(Unknown Source:95) 12-10 14:00:21.883 13537 13674 E Unity: at android.os.Handler.dispatchMessage(Handler.java:103) 12-10 14:00:21.883 13537 13674 E Unity: at android.os.Looper.loop(Looper.java:214) 12-10 14:00:21.883 13537 13674 E Unity: at Z4D23 6D9A2D102C5FE6AD1C50DA4BEC50Z.unity3d.player.UnityPlayer$e.run(Unknown Source:20)

In order to understand what's wrong with your implementation actually you need to decompile your Kotlin code to Java code and implement the Unity part based on the Java code, not Kotlin .

Let's have a simple example of Kotlin code, which will print Logs, nothing more:

class MyKotlinClass(val name: String = "DEFAULT NAME") {
    fun callNormalFunc() {
        Log.d("UNITY", "callNormalFunc from Kotlin code")
    }
    companion object {
        @JvmStatic
        fun callStaticCompanionFunc(): MyKotlinClass {
            Log.d("UNITY", "callStaticCompanionFunc from Kotlin code")
            return MyKotlinClass("NEW NAME")
        }
    }
}

And the decompiled Java code looks like this:

public final class MyKotlinClass {
   @NotNull
   private final String name;
   @NotNull
   public static final MyKotlinClass.Companion Companion = new MyKotlinClass.Companion((DefaultConstructorMarker)null);

   public final void callNormalFunc() {
      Log.d("UNITY", "callNormalFunc from Kotlin code");
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public MyKotlinClass(@NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
   }

   // $FF: synthetic method
   public MyKotlinClass(String var1, int var2, DefaultConstructorMarker var3) {
      if ((var2 & 1) != 0) {
         var1 = "DEFAULT NAME";
      }

      this(var1);
   }

   public MyKotlinClass() {
      this((String)null, 1, (DefaultConstructorMarker)null);
   }

   @JvmStatic
   @NotNull
   public static final MyKotlinClass callStaticCompanionFunc() {
      return Companion.callStaticCompanionFunc();
   }

   public static final class Companion {
      @JvmStatic
      @NotNull
      public final MyKotlinClass callStaticCompanionFunc() {
         Log.d("UNITY", "callStaticCompanionFunc from Kotlin code");
         return new MyKotlinClass("NEW NAME");
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

As we can see from the decompiled code actually we can call the function marked as @JvmStatic in two ways. First directly from our object and second using the Companion object.

Unity code this using this call will look like:

public class KotlinCallScript : MonoBehaviour {

    private AndroidJavaObject _object;
    private AndroidJavaClass _staticClass;
    // Start is called before the first frame update
    private void Start () {
        _object = new AndroidJavaObject ("com.hardartcore.kotlin.MyKotlinClass");
        _staticClass = new AndroidJavaClass ("com.hardartcore.kotlin.MyKotlinClass");
        var defaultName = _object.Call<string> ("getName");
        Debug.Log ("START GET DEFAUL NAME: " + defaultName);
    }

    public void CallNormalFunction () {
        _object.Call ("callNormalFunc");
    }

    public void CallStaticFunction () {
        var companionObject = _staticClass.GetStatic<AndroidJavaObject> ("Companion");
        var newName = companionObject.Call<AndroidJavaObject> ("callStaticCompanionFunc").Call<string> ("getName");
        Debug.Log ("CALL STATIC FUNCTION NEW NAME: " + newName);
    }

    public void CallSecondWay () {
        var kotlinObject = _object.CallStatic<AndroidJavaObject> ("callStaticCompanionFunc");
        var newName = kotlinObject.Call<string> ("getName");
        Debug.Log ("CALL SECOND WAY NEW NAME: " + newName);
    }

}

It's up to you which way you will prefer.

My suggestion is to look at your decompiled Java code, post it here so we can understand why it's not working.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM