Unity Package
This post is part of a series on Android Unity Package Development. If you’re interested in following along from the beginning, I recommend you start here
Package Setup⌗
With the Android plugin complete, let’s switch focus to the Unity side of the package. A local copy of the package can be added to an existing Unity project by adding a file-based reference to the project’s package manifest. This can be be done through either the Unity Package Manager UI or by modifying Packages/manifest.json
in the target project. The specified path should point to the folder containing package.json
.
"com.dcgoodnow.androidunitypackage": "file:<path>/<to>/<package>"
If the package has been added successfully, you should see something like this in the Package Manager window:
Before creating any scripts, define an assembly reference in the Scripts
folder of the package (Right-click in the Scripts folder -> Create -> Assembly Definition). This can help improve compile times and ensure that your IDE will recognize the package for script development.
Java Wrapper Class⌗
The Unity package will have two scripts. First, create a wrapper class called Toaster
to simplify communication with the Java class you created previously. This is a plain C# class, so if it was created inside the Unity Editor, you can remove the Monobehaviour base class and any autogenerated Unity lifecycle methods. In the class, define some constant strings based on the naming in the Java class as well as a reference to the underlying Java object.
private const string CLASS_NAME = "com.dcgoodnow.androidunityplugin.Toaster";
private const string METHOD_SHOW_TOAST = "ShowToast";
private readonly AndroidJavaObject _javaObject;
In the constructor, retrieve a reference to the application’s Unity activity, then pass it as an argument to the constructor of the Java class.
public Toaster()
{
using (var player = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var activity = player.GetStatic<AndroidJavaObject>("currentActivity"))
{
_javaObject = new AndroidJavaObject(CLASS_NAME, activity);
}
}
All C# objects created to reference Java objects implement the dispose pattern because they obtain a reference to the Java object in the JVM. In their dispose method, they release the reference to the Java object allowing it to be cleaned up by the garbage collector when no references remain. This pattern should be respected and implemented wherever a C# object holds a reference to an
AndroidJavaObject
.
Because the Toaster
class keeps a reference to the underlying Java object, it should implement the dispose pattern to ensure that the resources for the Java object are properly taken care of.
public class Toaster : IDisposable
{
...
public void Dispose()
{
_javaObject?.Dispose();
}
}
The Toaster
object in C# will provide it’s own method mirroring the ShowToast
method in the Java object.
public void ShowToast(string text)
{
_javaObject.Call(METHOD_SHOW_TOAST, text);
}
The high-level API provided by Unity for Java interop provides class member access either by string or by ID. When accessing a member by string,
AndroidJavaObject
automatically caches the member ID used for calls to the underlying Java object. If you need higher performance on the first invocation of the member, it may be better to retrieve the member ID directly after the object is constructed for later use.
Toast Behaviour⌗
The second class will be a simple Monobehaviour to give easy editor access to the Toast
class.
public class ToastBehaviour : MonoBehaviour
{
private Toaster _toaster;
private void Awake()
{
_toaster = new();
}
private void OnDestroy()
{
_toaster.Dispose();
}
public void ShowToast(string message)
{
_toaster.ShowToast(message);
}
}
There are a few things to take note of in the above class. First, the creation of the Toaster
object is delayed until the Awake()
Unity lifecycle method. This is because Toaster
requires the Unity Android activity to be created before it is constructed. Also, because this is a Monobehaviour holding a reference to a disposable class, Dispose()
needs to be called in the OnDestroy() method.
Now, clients of the package can add a ToastBehaviour
to a GameObject or create a new Toast
object directly in their own scripts. For example, you can add a button and connect the On Click
event to show a toast with a predefined message:
In the the next article, I’ll show how the package can be built and published to a package registry automatically using Github Actions.