Skip to main content
Version: 14 Oct 2024

Callback via Unity SendMessage

Overview

Here we will demonstrate how to receive callbacks in Unity from your Android Plug-in. This is so your sensor values in Unity only update when the sensor values update from the plug-in. It also avoids having to query the plug-in in Unity every frame via the Update or a similar function, which can vary in frequency depending on your apps performance.

Obtaining the classes.jar file from Unity

In order to use various Unity classes and functions in your Android plug-in, you will first need to locate and copy the classes.jar file into your Android Studio project.

  1. Navigate to your Unity install location. An easy way to do this is to Open the Unity Hub and go to Installs, find your Unity version, select the cogwheel, and select Show in Explorer.
  2. Once in your Unity install location, navigate to this file path: Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes.
  3. Copy the classes.jar file in this folder.

Importing the classes.jar file into Android Studio project

Now that we have the file from Unity, we need to bring it into our Android Studio project.

  1. Navigate to the root of your Android Studio project, then your plug-in module. For this example, the filepath is ExampleSensorPlugin\AccelerometerLibrary
  2. At this location, create a new folder called libs
  3. Paste the classes.jar file we obtained previously into your new libs folder.
  4. Next, we need to add classes.jar to our list of dependencies inside the build.gradle.kts file. In Android Studio, open the build.gradle.kts file that is inside your Library Module. In our example, the file path is ExampleSensorPlugin\AccelerometerLibrary\build.gradle.kts
  5. At the bottom of the file, look for the dependencies section. Add this line to the file compileOnly(files("libs\\classes.jar")) Your dependencies section should now look like this
dependencies {

implementation(libs.appcompat)
implementation(libs.material)
compileOnly(files("libs\\classes.jar"))
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
  1. Finally, open up the AccelerometerListener.java file we created previously and navigate to the top of the script. Now, import the UnityPlayer package with import com.unity3d.player.UnityPlayer;
package com.magicleap.accelerometerlibrary;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.unity3d.player.UnityPlayer;

Sending sensor value updates via UnityPlayer.UnitySendMessage

info

UnityPlayer.UnitySendMessage takes 3 parameters. The first is the name of the gameObject that has a script on it with a matching function name of the 2nd parameter. The 2nd parameter is the function name that will be used to receive the sensor updates. The 3rd is the message or data you want to send.

  1. In our Android Library, let's modify the startSensorListening function to take 2 new parameters, gameObjectName and callbackFunctionName. Also, be sure to save these in String variables so we can use them later!
    private String unityObjectName;
private String unityFunctionName;
public void startSensorListening(String gameObjectName, String callbackFunctionName)
{
//initialize sensorManager
sensorManager = (SensorManager) unityActivity.getSystemService(Context.SENSOR_SERVICE);
//use sensorManager to obtain the devices default accelerometer
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//register this sensor to begin listening and receiving SensorEventListener callbacks
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
//set string variables for sending messages to Unity
unityObjectName = gameObjectName;
unityFunctionName = callbackFunctionName;
}
  1. Next, we will use these new values to send updates to Unity when our sensors receive value updates. In the onSensorChanged function, add the following:
    UnityPlayer.UnitySendMessage(unityObjectName, unityFunctionName, Arrays.toString(event.values));
    The AccelerometerListener file now looks like this:
package com.magicleap.accelerometerlibrary;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.unity3d.player.UnityPlayer;
import java.util.Arrays;

public class AccelerometerListener implements SensorEventListener
{
private static Activity unityActivity;
private SensorManager sensorManager;
private Sensor accelerometer;
private String unityObjectName;
private String unityFunctionName;


public static void setUnityActivity(Activity _activity)
{
unityActivity = _activity;
}

@Override
public void onSensorChanged(SensorEvent event)
{
UnityPlayer.UnitySendMessage(unityObjectName, unityFunctionName, Arrays.toString(event.values));
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{

}


public void startSensorListening(String gameObjectName, String callbackFunctionName)
{
//initialize sensorManager
sensorManager = (SensorManager) unityActivity.getSystemService(Context.SENSOR_SERVICE);
//use sensorManager to obtain the devices default accelerometer
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//register this sensor to begin listening and receiving SensorEventListener callbacks
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
unityObjectName = gameObjectName;
unityFunctionName = callbackFunctionName;
}
}
  1. Be sure to build this library and update it in your Unity project!
    In the Android Studio toolbar, navigate to Build->Make Module 'ExampleSensorPlugin.AccelerometerLibrary'.
  2. In a file explorer, navigate to
    ExampleSensorPlugin\AccelerometerLibrary\build\outputs\aar\AccelerometerLibrary-debug.aar
    Copy this file!
  3. Paste this file inside your Unity project at Assets\Plugins\Android. If you already have an AccelerometerLibrary-debug file at this location, delete it before you paste the new version.

Receiving sensor updates in Unity

Now that our library is sending updates to Unity, we need a way to receive these updates.

  1. Inside the AccelerometerPluginManager script, let's add a new function that will receive the sensor updates. All this function will do is Log the values to the console.
    public void OnAccelerometerSensorUpdate(string message)
{
Debug.Log("Accelerometer values: " + message);
}
  1. Now, modify the call to startSensorListening to accomodate for the 2 new parameters we added to it.
pluginInstance.Call("startSensorListening", gameObject.name, "OnAccelerometerSensorUpdate");

Your AccelerometerPluginManager script should now look like this:

using UnityEngine;

public class AccelerometerPluginManager : MonoBehaviour
{
private AndroidJavaClass unityClass;
private AndroidJavaObject unityActivity;
private AndroidJavaObject pluginInstance;

// Start is called before the first frame update
void Start()
{
unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
unityActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity");
pluginInstance = new AndroidJavaObject("com.magicleap.accelerometerlibrary.AccelerometerListener");

if (pluginInstance != null)
{
Debug.Log("Plugin Instance Loaded!");
pluginInstance.CallStatic("setUnityActivity", unityActivity);
pluginInstance.Call("startSensorListening", gameObject.name, "OnAccelerometerSensorUpdate");
}
}

public void OnAccelerometerSensorUpdate(string message)
{
Debug.Log("Accelerometer values: " + message);
}
}

Build and run on ML2 device

We've made all the updates necessary for us to receive updates from our Android plugin in our Unity project via UnityPlayer.UnitySendMessage.
You can now build and run the project on your ML2 device!
The sensor value updates can be observed from the Log console.