Home / Tutorial / Android Native Plugin: From Android Studio to Unity

Android Native Plugin: From Android Studio to Unity

/
/
/
6122 Views

In this tutorial, we will see how to create and import into Unity an Android Native Plugin. 

Android Native Plugin

“Native Plugins are platform-specific native code libraries.” (Unity Manual)

An Android Native Plugin allows accessing Android specific features. For example, we need a plugin to use features such as native dialogs and pickers, toasts, the mobile camera, among others.

In order to create and use a native plugin, we need to set up a communication between Unity and the desired platform.

On the platform side, we write the functions in its specific language. In Android, the language is Java and in iOS it’s Objective-C.

In this tutorial, we will build an Android Native Plugin and in a later one, we will approach iOS.

Outline

  1. The Android Studio Project

  2. The Android Native Code

  3. The Unity Project

  4. The Unity Code

  5. Run and be amazed!

Step 1. The Android Studio Project

The New Project Wizard

Create a new Android Studio Project and choose the Application name and your Company Domain.

Android Native Plugin: Configure your new project with an Application name and Company Domain.
Image 1. Configure your new project with an Application name and Company Domain.

Next, you need to choose the Minimum SDK version of the project. You can just leave the suggested version.

Android Native Plugin: Select the Minimum SDK version.
Image 2. Select the Minimum SDK version.

And finally, int the last step you may add a default activity to the project. In this case, I suggest you select the “Add No Activity” option.

Android Native Plugin: Select the Add No Activity option.
Image 3. Select the Add No Activity option.

The build.gradle file

In order to import the plugin into Unity, we need to convert the app module into a library.

Open the module build.gradle file.

The build.gradle file of the app module is the one with (Module: app) at the end. The another one is the build file of the entire project.

Android Native Plugin: Open the app module build.gradle file.
Image 4. Open the app module build.gradle file.

Make the following changes to build.gradle file.

To import the library into Unity we need to compile our .java classes into .class files and then package it into a .jar file

Let’s create a couple of new gradle tasks to package the compiled classes into a .jar archive.

At the end of build.gradle file add the following code.

Here is a breakdown of the code:

task exportJar: copies the .jar file from the default to a new release directory and renames it into AddComponentNative.jar (or whatever you wanna call it).

task deleteOldJar: deletes the .jar file on the release directory.

exportJar.dependsOnmakes the exportJar task run after the successful conclusion of the deleteOldJar and the build task. So each time it runs, it will delete the last .jar, build the module and then copy and rename the new .jar file.

Now, open the Gradle Tool Window. Expand it until you reach the following path UnityNativePlugin/:app/Tasks/other and run (double-click) the exportJar task.

You can expand the Gradle Tool Window by selecting it on the right side of the IDE or by selecting it in the menu View/Tool Windows/Gradle.

Android Native Plugin: Expand the hierarchy to reach the exportJar task.
Image 5. Expand the hierarchy to reach the exportJar task.

When the task completes without errors (which I hope it happens) you should have a new .jar file inside <project directory>/app/release.

Android Native Plugin: Check the exported .jar file on the release directory.
Image 6. Check the exported .jar file on the release directory.

Step 2. The Android Native Code

The classes.jar file

First, you need to import the classes.jar shipped with Unity Android into the project.

You can find it at the following locations:

Windows

C:\Program Files\Unity\Editor\Data

Mac

 /Applications/Unity in a sub-folder called PlaybackEngines/AndroidPlayer/Variations/mono or il2cpp/Development or Release/Classes/

Copy and paste the classes.jar file into the libs directory inside the app module.

Android Native Plugin: Change to the Project View.
Image 7. Change to the Project View.

Note: You can change the view to Project to see the libs directory. By default, you should be using the Android view.

Android Native Plugin: Paste the classes.jar file into the libs directory.
Image 8. Paste the classes.jar file into the libs directory.

We also need to include the classes.jar in the plugin file. So, open the build.gradle and add the following line.

Finally, we have everything in place to actually start coding.

In this tutorial, we will see different examples on how to pass data back and forth between Android and Unity. But the idea is to only show the basics of implementing such communication.

In this way, the given examples will not solve real necessities, like showing a native dialog. But will show the fundamental concepts of creating an Android Native Plugin.

On a later post, we will see some examples on how to use an Android Native Plugin to solve “real life” situations. So just bear with me.

Return to Android view of the project so you have less “clutter” on the structure view.

The Examples.java file

Create an Examples.java class.

Android Native Plugin: Press the RMB on the package name to access the context menu and create a New Java Class.
Image 9. Press the RMB on the package name to access the context menu and create a New Java Class.
Android Native Plugin: Create a new New Class called Examples of Kind Class.
Image 10. Create a new New Class called Examples of Kind Class.

First, let’s add a couple of methods to log something on the Android side.

Create a new public method called sayMyname that returns void and accepts no arguments. And create another public method called sayItLikeJesse that returns void and accepts a string argument.

You actually don’t need the TAG variable but I prefer to define a variable for the log tag.

Both these methods just log something into the console but don’t return anything. So let’s add another one that returns a value when called.

Whenever we call this function it will randomly return one of the results.

The getRandomNumber function is just a little add-on to make this example somewhat more entertaining (yeah and I know, it’s still booooring).

The UnitySendMessage method

Now,  imagine the following example:

You need a function that shows the Native Date Picker Dialog and returns the selected date to the Unity side.

Keep this example in mind because we will talk about it in a future post.

For this example, we cannot use something like the last function because the selected date is not available when called. So, we need a function to show the native dialog, but since the result is not available, we also need a way to pass back the selected date into Unity when ready.

The java class com.unity3d.player.UnityPlayer has a static method called UnitySendMessage that allow sending data from java into Unity.

Method signature breakdown:

  1. Name of the target GameObject: the one with script to receive the data;
  2. Name of the Method that will receive it: it needs to exist in any script applied to the target GameObject;
  3. Message string argument: the data we want to send. 

The receiving method must correspond to the following signature MethodName(string message).

Let’s create a new method and use the UnitySendMessage to send data back into Unity.

It is not necessary to pass the GameObject and Method name as arguments but it is always more flexible.

So far, all the examples are of non-static methods, therefore an instance of the Examples class is required.

The last example is about calling a static method on Java from the Unity side.

Create the following method.

And finally, the Java side is complete.

Try to export the .jar to check if everything is working.

Here is the complete Examples.java file.

3. The Unity Project

Create a new Unity Project, a new scene and inside the Assets folder add the following directories Plugins/Android.

Android Native Plugin: Create the Plugins/Android path.
Image 11. Create the Plugins/Android path.

The AddComponentNative.jar file needs to go inside the Android folder. You can either drag it from the release folder into Unity, or the coolest way is to modify the copy path on the Android build.gradle so the .jar is automatically copied into the Unity folder when built. Let’s go with the latter one.

Go back to Android Studio, open the build.gradle file and make the following change.

Test to see if everything is running smoothly.

Android Native Plugin: Check if you have the .jar file inside the Plugins/Android folder.
Image 12. Check if you have the .jar file inside the Plugins/Android folder.

4. The Unity Code

Now that we have the .jar file inside Unity we need a new component to interact with it. Create a new MonoBehaviour called NativeAndroid.cs (or whatever you want).

Android Native Plugin: Create a new MonoBehaviour.cs.
Image 13. Create a new MonoBehaviour.cs.

Assign the new script to any GameObject on the scene.

Android Native Plugin: Apply it to any GameObject on your scene.
Image 14. Apply it to any GameObject on your scene.

Let’s open the new script and add some methods to call the ones in Java.

In order to access the Java code, we need to use the JNI (Java Native Interface) which allows interacting with Java or the JVM from native code. But, we don’t need to use JNI directly. Instead, we have a couple of helper classes, the AndroidJavaObject and the AndroidJavaClass, to ease the interaction with JNI. These two classes automate most tasks (we won’t get into much detail on this topic but feel free to check what the Unity Manual has to say about it).

Create a method to call the sayMyName function on Android.

Unity (C#)

Android (Java)

Using the AndroidJavaObject we create a new instance of the desired Java class (Examples.java). To create the new instance we need to pass the entire package name to the object constructor.

Then we use the object reference to call any method that we’ve defined on that class (sayMyName).

The method name must be exactly the same as the one defined on Java otherwise is not found.

According to the Unity Manual, we should keep all AndroidJavaObject and AndroidJavaClass instances inside using() {} statements to make sure the garbage collector deletes them as soon as possible.

The second function sayItLikeJesse, is similar to the last one, but accepts a string argument. You can call the AndroidJavaObject.Call method with any number of arguments. Just make sure it corresponds to the ones declared in Java.

Unity (C#)

Android (Java)

I’m passing the text of an Input Field as the string argument. I’ll show you the simple UI I’ve created to test this functions.

The next method, whoAreYou, has a return value of type string. To call methods with non-void return types we use a generic to represent the desired return type.

Add the following method after the last one.

Unity (C#)

Android (Java)

The method whatsThePoint will send data into Unity using the UnitySendMessage. To receive the data on Unity, we need to declare a method with signature MethodName(string message) on a MonoBehaviour. And we must assign the script to a GameObject on the scene.

The UnitySendMessage needs to know the name of the GameObject and the name of the Method. You can either define it on Java or pass it as an argument from Unity. In this case, let’s use the latter one.

Unity (C#)

Android (Java)

We’re calling the method whatsThePoint in Java, passing as arguments the GameObject name – NativeBridge – and the Method name – WhatsThePointReceived.

The data sent from UnitySendMessage is received by the WhatsThePointReceived method which has a signature of MethodName(string message).

In this case, we declared the receiving method on the same script as the one which is calling the function but it we could declare it anywhere. As long as we assign it to a GameObject on the scene and pass the correct names to the UnitySendMessage.

The last method is different from the other ones because it’s a static method. To call a static method, instead of an AndroidJavaObject, we need an instance of an AndroidJavaClass.

Unity (C#)

Android (Java)

The code is similar to the last examples, but instead of AndroidJavaObject we use the AndroidJavaClass and instead of Call we use the CallStatic method.

Finally, the code is ready. Let’s run it!

Here is the full version of the C# script.

5. Run and prepare to be amazed!

In order to test these functions we need to run it on an actual Android device. So, we’ve created a basic UI with some Buttons, Labels and Input Fields. You probably, already know how to create this, so the result should look something like the following image.

Android Native Plugin: Create a UI to test the plugin.
Image 15. Create a UI to test the plugin.

Each button is calling the function with the same name on the script NativeAndroid.cs assigned to the GameObject NativeBridge.

The “Say My Name”, “Say It Like Jesse” and “Say Out” just log some text into the Android Studio Logcat. The “Who Are You?” and “What’s the point?” receive a string value and show it on the labels underneath it.

To run the app on an Android device you need to change the platform on the Build Settings.

Android Native Plugin: Open the Build Settings (File/Build Settings) and change the Platform to Android.
Image 16. Open the Build Settings (File/Build Settings) and change the Platform to Android.

And then, assign a bundle identifier on the Player Settings.

Android Native Plugin: Open the Player Settings (Edit/Project Settings/Player) and set a Bundle Identifier on the Other Settings section.
Image 17. Open the Player Settings (Edit/Project Settings/Player) and set a Bundle Identifier on the Other Settings section.

Now you can build and run the project to test it on a real Android device.

To check the device logs on Android open the Android Studio, select the Android Monitor on the bottom of the screen and the logcat tab.

Android Native Plugin: Open the logcat on Android Studio.
Image 18. Open the logcat on Android Studio.

You can filter the logs by searching for the tag used in the Log.d function.

This is a very basic tutorial about building an Android Native Plugin for Unity. There are still more complex topics to explore such as extending the Android UnityPlayerActivity and actually accessing Android native features such as dialogs, toasts and the camera. But we believe that now it will be easier to expand into those topics since the fundamentals are already laid out.

Stay tuned.

  • Facebook
  • Twitter
  • Google+
  • Linkedin
  • Pinterest
  • Reddit

4 Comments

  1. I don’t know why but there is no “release” folder inside bundles folder, but there is a default folder so my code looks like this:

    task exportJar(type: Copy) {
    from(‘build/intermediates/bundles/default/classes.jar’)
    include(‘classes.jar’)
    into(‘release/’)
    rename(‘classes.jar’, ‘Porra.jar’)
    }

    btw thanks, great tutorial!

  2. Hallo. I have followed all the Instruction. But i have Problem with “What’s the point?” It send nothing and receive nothing.
    Can you help me regarding this Problem?

Leave a Comment

Your email address will not be published. Required fields are marked *