There are scenarios where we need to access devices or APIs using native libraries in Unity3D. When it comes to hardware API calls such as battery percentage or CPU usage on android builds, there is no direct way to access these on Unity3D.
In this section, we will create a native android plugin in .aar format and integrate it into the Unity3D application.
Getting Started
We can use asset store and unity packages for implementing additional functionalities for our unity projects. However, there are scenarios where there are no unity package or asset store assets available for native android modules. We can solve the problem by building a native android plugin using Android studio.
An Android library includes everything required to build an app, including source code, resource files, and an Android manifest. However, an Android library compiles into an Android Archive (AAR) file which can be used be as dependencies on projects.
Let’s start creating our native plugin.
Requirements
- Android Studio
- Unity3D
A. Android project setup
- Open Android Studio. Click on the new project button to create an android project. Select the Empty activity. Provide necessary information on the create new project window and click on the Finish button to create the project.
- After the gradle build, right click on the app folder then select New → Module option to create a new Android module. Select the “Android Library” template. Keep the remaining information as defaults (you can alter it if required). Click on the Finish button to create a new Android module.
Define your custom library
We can define a custom library to display the battery level information in the UI using the Toast method. Let’s begin with our native plugin to print a string passed from Unity3D.
- Right click on the module → java folder. Select New → Java Class and provide a valid name (In this example, we will use Alert).
- Add the following code to the Alert class. ```
package com.codemaker.mylibrary;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class Alert extends Application {
// our native method, which will be called from Unity3D
public void PrintString(Context context, final String message) {
//create / show an android toast, with message and short duration.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@override
public void run() {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
});
}
}
The Alert class has a `PrintString()` method which takes two arguments — context and string.
- Similarly, we can create one more class named `BatteryLevelIndicator` to provide the battery level information.
- Add the following code to the `BatteryLevelIndicator` class.
package com.codemaker.mylibrary;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
public class BatteryLevelIndicator extends Application {
// Return the battery level as a float between 0 and 1
// (1 being fully charged, 0 fulled discharged)
public float GetBatteryPct(Context context) {
Intent batteryStatus = GetBatteryStatusIntent(context);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;
return batteryPct;
}
// Return whether or not we're currently on charge
public boolean IsBatteryCharging(Context context) {
Intent batteryStatus = GetBatteryStatusIntent(context);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
return status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
}
private Intent GetBatteryStatusIntent(Context context) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
return context.registerReceiver(null, ifilter);
}
}
### Build native library
Press the “Make Project” button ( CTRL + F9 ) in the toolbar to build the Android Module.
![build aar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzojmiqfphnhvnjlrlz0.PNG)
After compilation, you will find an android archive file located under `app\<library-name>\build\outputs\aar` folder.
![aar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/44q1vhovoyfm3k1zmyp4.PNG)
## B. Use the native plugin inside the Unity3D project
- Create a new Unity3D project using Unity Hub.
![unity project 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79l77kao7ed3vvzgue72.PNG)
![unity project 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8xrkk8tb7ktw6p2xpe7.PNG)
- Open your Unity3D project and create the folder structure: Assets / Plugins / Android and Place the custom library (`mylibrary-debug.aar`) inside the folder.
![plugins](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lrlg6bwuhjcz71a5kkx.PNG)
### Native library integration
- Create a C# script to access the imported library from the app. For which, create a folder named Scripts . Add the script inside Scripts folder and name it NativeCodeRunner.cs . Add the following code to it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NativeCodeRunner : MonoBehaviour {
void Start() {
string msg = "Battery Level: " + (GetBatteryLevel() * 100) + "%";
ShowToast(msg);
}
//method that calls our native plugin.
public void ShowToast(string msg) {
if (Application.platform == RuntimePlatform.Android) {
// Retrieve the UnityPlayer class.
AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
// Retrieve the UnityPlayerActivity object
AndroidJavaObject unityActivity = unityPlayerClass.GetStatic("currentActivity");
// Retrieve the "Bridge" from our native plugin.
// ! Notice we define the complete package name.
AndroidJavaObject alert = new AndroidJavaObject("com.codemaker.mylibrary.Alert");
// Setup the parameters we want to send to our native plugin.
object[] parameters = new object[2];
parameters[0] = unityActivity;
parameters[1] = msg;
// Call PrintString in bridge, with our parameters.
alert.Call("PrintString", parameters);
}
}
public float GetBatteryLevel() {
if (Application.platform == RuntimePlatform.Android) {
AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayerClass.GetStatic("currentActivity");
AndroidJavaObject alert = new AndroidJavaObject("com.codemaker.mylibrary.BatteryLevelIndicator");
object[] parameters = new object[1];
parameters[0] = unityActivity;
return alert.Call("GetBatteryPct", parameters);
}
return -1f;
}
}
- Create an Empty GameObject and add the NativeCodeRunner script to it.
![Empty GameObject](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5h04ksmw58jh6g1rtu7.PNG)
### Setup the project
- Open project settings (File → Build settings) and switch the target platform to Android.
![target platform](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg19fx9jc9z5jfbc03yi.PNG)
- Add the current scene to the build by clicking on the Add Open Scenes button.
![current scene](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gz4m5iz8lugkv3a08jw8.PNG)
- Provide a package name and change the Minimum target version to Android 7.1 ‘Nougat’ (API level 25).
![Minimum target version](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qprzml8v64eaovhuxr0s.png)
- The latest android version has some security restrictions to access the activity from another app. If the android version is greater than 30 then set android:exportedto “true" to allow other apps to access it. For which, enable the custom main Android manifest file from the Publishing settings under the Player settings.
![manifest file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oz98j40tcn9a4dt689ku.PNG)
- Open the AndroidManifest.xml file from the Plugins/Android folder. Add android:exported=”true” code in the Activity tag and remove the following comment.
The final AndroidManifest.xml will look as the following,
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector"
android:exported="true">
### Run the app
Connect the mobile device via USB cable and click on the Build and Run button to run the app on the target device. Ensure that USB debugging is enabled on your device.
![output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1wt94tdwr9a2j66kxy7.jpeg)
There you have it! Your own android app with a custom native android library in Unity 3D :)
Thanks for reading this article.
Thanks Gowri M Bhatt for reviewing the content.
If you enjoyed this article, please click on the heart button ♥ and share to help others find it!
The article is also available on [Medium](https://medium.com/@codemaker2016/create-and-import-a-custom-android-library-in-unity3d-ed7f848c6c54).
The full source code for this tutorial can be found here,
[GitHub - codemaker2015/unity-android-native-library-demo](https://github.com/codemaker2015/unity-android-native-library-demo)
Top comments (2)
Hello, could you help us, I followed the document but it is giving an error after publishing an internal test, I wanted to make it work and then try to create a lib that identifies an application other than the game being run, to identify a mod or hack