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:

Screenshot showing package manager window with the correctly referenced local package.
Local Package in Package Manager

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:

Screenshot showing a Toastbehaviour attached to a Button component. The Button's On Click event calls the ShowToast method with the string 'This is a toast!'
Button Toast Example

In the the next article, I’ll show how the package can be built and published to a package registry automatically using Github Actions.

Additional Resources