In continuation to previous my previous blogs, where we learnt about how can we use AndroidJavaClass and AndroidJavaObject to achieve simple native Android functionality in Unity, We will move one step ahead and see how can we typecast one Android object to another in Unity. If you have not read the previous blogs, I would strongly recommend to read them first. You can read them on the links below.
AndroidJavaClass is the Unity representation of a generic instance of java.lang.Class whereas AndroidJavaObject is the Unity representation of a generic instance of java.lang.Object. We can create a Java class reference object using AndroidJavaClass and an object of that class using AndroidJavaObject but what if we have to typecast one Android object to another of Android class only. Let's take an example where we need to get the Parcelable extra from intent in Android and typecast it to Uri object of Android itself. Android share data between activities using Parcelable. We can put anything extra which is Parcelable into the Bundle or directly into the Intent and can receive it using getParcelableExtra. The Java or Kotlin equivalent code for the same will be
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
val uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as Uri
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
Looking at the Java code it's easy to understand that Java directly converted it into Uri object but that is not possible in the case of Kotlin as Koltin is not a strongly typed language and the data type of the variable is decided at runtime but not at compile time. Same in the case of C# also. We have to typecast it to Uri manually. But since the object first received will be a Parcelable object the question is can we convert it from one object to another by just using AndroidJavaClass and AndroidJavaObject?
The answer is YES
We can convert one object to another by just using AndroidJavaClass and AndroidJavaObject. We can use java.lang.Class and its two functions forName and cast to typecast it to the desired Android/Java object. forName is a static function that returns the Class object associated with the class or interface with the given string name. cast casts the Class object to represent a subclass of the class represented by the specified class object. Checks that that the cast is valid, and throws a ClassCastException if it is not. If this method succeeds, it always returns a reference to this class object. The equivalent casting code would be,
public AndroidJavaObject castToJavaObject(AndroidJavaObject source, string className)
var clazz = new AndroidJavaClass("java.lang.Class");
var destClass = clazz.CallStatic<AndroidJavaObject>("forName", className);
return destClass.Call<AndroidJavaObject>("cast", source);
What we have done here is,
- Created java.lang.Class reference object.
- Used its static method forName which returns the required class or interface name reference object.
- Used non-static method cast which ultimately casts it to the required class or interfaces reference object.
And to use it for our use case,
//get the context of current activity
AndroidJavaObject context = unityActivity.GetStatic<AndroidJavaObject> ("currentActivity");
//get intent from the current context
AndroidJavaObject intent = context.Call<AndroidJavaObject>("getIntent");
//get parcelable extra from intent
AndroidJavaObject parcelable = intent.Call<AndroidJavaObject>("getParcelableExtra", "android.intent.extra.STREAM");
//Cast it to Uri type
AndroidJavaObject uri = castToJavaObject(parcelable, "android.net.Uri");
The entire code put together will be