- Home /
The question is answered, right answer was accepted
Calling a java method in android from Unity
Our project uses it's custom video player script and makes a call to the android side within the Update function - to update audio angles as this is a 360 degree video player. There is a large amount of garbage collection going on per frame based on profiler sample results. We are passing two float arguments to the java function. Does anyone know why there is a lot of GC happening in this call? Pasting the code here:
horizontal = eulerAngles [1] * Mathf.Deg2Rad;
vertical = eulerAngles [0] * Mathf.Deg2Rad;
_JO.Call(SET_AUDIO_ANGLE, -horizontal, vertical);
Answer by Bunny83 · Oct 25, 2017 at 11:02 PM
Two things: First the signature of the Call method is
public void Call(string methodName, params object[] args)
That means each time you call it you will create a new "object[]" with your two parameters. The second thing is your two float parameters need to be boxed in order to be stored in the array.
Unfortunately there's nothing you can do against the boxing beside avoid calling the method when the angles haven't changed. You may use a minimum difference threshold. The object array can be cached. So create the array once and reuse it.
edit
Well there's actually a way around that boxing problem. The "Call" method is rather convenient to use but creates a lot of "internal garbage". The actual JNI interface doesn't take an "object[]" but rather a "jvalue[]". jvalue is a struct with overlapping fields for the various types.
So the solution would be to:
Use
AndroidJNIHelper.GetMethodID
to get back an IntPtr to the method id . You have to pass the native class reference, the method name, the object[] with the parameters and "false" (since it's an instance method, not a static method). You store that reference next to your actual "_JO".Beside that you create a jvalue array of size 2. This array should be cached and reused.
Finally you can call the method with
AndroidJNI.CallVoidMethod
. You need to pass the native object pointer which you get from_JO.GetRawObject()
, the method ID we stored and the jvalue array. Before you call that method of course you have to update the values in the array. The struct type has a field named "f" which is of type float. There you can assign your two values.
So it would look something like this:
AndroidJavaObject _JO;
System.IntPtr methodID;
jvalue[] parameters = new jvalue[2];
void Init()
{
// make sure your _JO object is already created at this point.
//methodID = AndroidJNIHelper.GetMethodID(_JO.GetRawClass(), SET_AUDIO_ANGLE, new object[]{0f,0f}, false);
// just noticed that there is also a GetMethodID version that takes a signature string. This avoids
// the temp object array. The array is only used to create that signature string from the types
methodID = AndroidJNIHelper.GetMethodID(_JO.GetRawClass(), SET_AUDIO_ANGLE, "(FF)V", false);
}
void UpdateAudioAngles(Vector3 aEulerAngles)
{
parameters[0].f = -aEulerAngles.y * Mathf.Deg2Rad;
parameters[1].f = aEulerAngles.x * Mathf.Deg2Rad;
AndroidJNI.CallVoidMethod(_JO.GetRawObject(), methodID, parameters);
}
Note: Be aware that "CallVoidMethod" might throw an exception. Unity actually has an internal class (in all their wiseness they makes all useful classes internal) that wraps the call in a try catch block and performs some special Java exception handling in case of an exception. This is missing in this case. AS long as there is no error in your java method you should be fine. Don't ask me what happens when an exception is thrown. You might just try it ^^.
ps: i guessed "SET_AUDIO_ANGLE" is actually a string constant that holds the method name, right?
Thanks a lot for your answer! It brought down GC allocation from 250 bytes per frame to 0.
Great to hear ^^. Recently i did not do any android development (my last android build was three years ago). Though i always was interested in the JNI interface. It provides direct access to the Java world (even though it's a hell to work with ^^).
Unity's JNI wrappers are intended to be "easy to use" but in turn generate a lot garbage. For example they gave the "Call" method a generic parameter to choose the return type. However all value types would get boxed in their wrapper code while the Android JNI bridge actually provides seperate call methods with the right managed return type (like "CallBoolean$$anonymous$$ethod", "CallByte$$anonymous$$ethod", "CallShort$$anonymous$$ethod", ...).
Follow this Question
Related Questions
How to save and load a json file on android 1 Answer
Upgrade from 5.0.1 to 5.1.2f1, can't build in android now 1 Answer
pick image from android gallery 3 Answers
Failed to re-package resources. 0 Answers
My game crahses when I use it on a phone 0 Answers