DevUA

Unity3D developer. Currently developing GetSocial Unity SDK and working on various Unity open-source projects.

Meta


Type casting AndroidJavaObject in Unity from C#

This article will explain how to cast AndroidJavaObject to different java type purely in C# in Unity.

Taras LeskivTaras Leskiv

When writing my Android Goodies Plugin for Unity Asset Store I encountered the issue of casting AndroidJavaObject to another class inside of C#.

When developing Android applications in Unity, there is a high-level JNI API to work with java code straight from C# (those are AndroidJavaObject , AndroidJavaClass  and AndroidJavaProxy  classes). Using those three classes you can deal with 99% of tasks that you need to achieve while writing an Android plugin.

A few straightforward examples:

Getting Unity Player Activity:

// Getting main activity of Unity game on Android
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

Property that gets current time in millis from java:

// Getting current time in millis from java
public static long CurrentTimeMillis
{
    get
    {
        using (var system = new AndroidJavaClass("java.lang.System"))
        {
            return system.CallStatic<long>("currentTimeMillis");
        }
    }
}

Using this you can execute pretty much everything you need. Let’s now dive into the problem.

When writing my plugin in C# I am trying to write as little java code as possible for easier maintenance of the project and for the developer using my plugin to be able to browse the C# code instead of decompiling my jars. This is the tricky part that I had to do, to execute this piece of Android java code in pure C#:

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

The main problem here is after I can AndroidJavaObject  as a result of getSystemService(Context.TELEPHONY_SERVICE)  it is of type of java.lang.Object  and I cannot execute TelephonyManager  methods on this object. There is no built-in support for object casting so we have to use java reflection to perform this task, java.lang.Class.cast() specifically. First we need to obtain the java.lang.Class object instance for the target class (we can’t use java class literal in C#):

public static AndroidJavaObject ClassForName(string className)
{
    using (var clazz = new AndroidJavaClass("java.lang.Class"))
    {
        return clazz.CallStaticAJO("forName", className);
    }
}

Now extension method for casting goes like this:

// Cast extension method
public static AndroidJavaObject Cast(this AndroidJavaObject source, string destClass)
{
    using (var destClassAJC = ClassForName(destClass))
    {
        return destClassAJC.Call<AndroidJavaObject>("cast", source);
    }
}

Now we can cast our telephony object from java.lang.Object to android.telephony.TelephonyManager :

private static AndroidJavaObject GetSystemService(string name, string serviceClass)
{
    try
    {
        var serviceObj = AGUtils.Activity.CallAJO("getSystemService", name);
        return serviceObj.Cast(serviceClass);
    }
    catch (Exception e)
    {
        Debug.LogWarning("Failed to get " + name + " service. Error: " + e.Message);
        return null;
    }
}
// Finally getting the telephony service
public static AndroidJavaObject TelephonyService
{
    get
    {
        return GetSystemService(TELEPHONY_SERVICE, TelephonyManagerClass);
    }
}

So now we can do whatever we want with our TelephonyService object and call methods we need.

P.S. Also remember that AndroidJavaObject , AndroidJavaClass  implement IDisposable interface so don’t forget to all Dispose() when you are done with them or use using  block which will be called automatically after object goes out of scope.

Unity3D developer. Currently developing GetSocial Unity SDK and working on various Unity open-source projects.