DEV Community

Cover image for Create and import a custom android library in Unity3D
Vishnu Sivan
Vishnu Sivan

Posted on • Updated on

Create and import a custom android library in Unity3D

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.

project creation 1
project creation 2
project creation 3

  • 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. android library 1 android library 2

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). module 1 module 2
  • 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();
            }
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Build native library

Press the “Make Project” button ( CTRL + F9 ) in the toolbar to build the Android Module.
build aar
After compilation, you will find an android archive file located under app\<library-name>\build\outputs\aar folder.
aar

B. Use the native plugin inside the Unity3D project

  • Create a new Unity3D project using Unity Hub. unity project 1 unity project 2
  • Open your Unity3D project and create the folder structure: Assets / Plugins / Android and Place the custom library (mylibrary-debug.aar) inside the folder. plugins

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<AndroidJavaObject>("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<AndroidJavaObject>("currentActivity");        
       AndroidJavaObject alert = new AndroidJavaObject("com.codemaker.mylibrary.BatteryLevelIndicator");            
       object[] parameters = new object[1];
       parameters[0] = unityActivity;
       return alert.Call<float>("GetBatteryPct", parameters);
   }
   return -1f;
 }
}
Enter fullscreen mode Exit fullscreen mode
  • Create an Empty GameObject and add the NativeCodeRunner script to it. Empty GameObject

Setup the project

  • Open project settings (File → Build settings) and switch the target platform to Android. target platform
  • Add the current scene to the build by clicking on the Add Open Scenes button. current scene
  • Provide a package name and change the Minimum target version to Android 7.1 ‘Nougat’ (API level 25). Minimum target version
  • 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
  • Open the AndroidManifest.xml file from the Plugins/Android folder. Add android:exported=”true” code in the Activity tag and remove the following comment.
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
Enter fullscreen mode Exit fullscreen mode

The final AndroidManifest.xml will look as the following,

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    <application>
        <activity 
            android:name="com.unity3d.player.UnityPlayerActivity"
            android:theme="@style/UnityThemeSelector"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>
Enter fullscreen mode Exit fullscreen mode

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
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.

The full source code for this tutorial can be found here,

GitHub - codemaker2015/unity-android-native-library-demo

Top comments (2)

Collapse
 
flashgamestudioltda profile image
FlashGameStudioLtda

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

Collapse
 
flashgamestudioltda profile image
FlashGameStudioLtda

Image description