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.