How to use Android Java .aar library?

I googled this quite a bit, but came out empty handed. There were some links but those related to source code version of UE. Before going down that path I decided to asks here whether there is some simple way to add and access an .aar library in my UE 4.20 Android project?

I found this announcement telling that aar files can be registered e.g. in aar-imports.txt. It continues about gradle files, but it remains a bit unclear whether something more needs to be added there or not. I really wish there were a solid document about using .aar files!

Based on some forum advice, I inserted my lib into aar-imports.txt file, like this

repositories $(ANDROID_HOME)/extras
repositories $(ENGINEDIR)/Source/ThirdParty/Android/extras
com.google.android.gms,play-services-auth,11.8.0
com.google.android.gms,play-services-games,11.8.0
com.google.android.gms,play-services-nearby,11.8.0
com.google.android.gms,play-services-plus,11.8.0
com.mycompany.myproduct.mylib,mylib-release,1.0

and inserted the lib under C:\NVPACK\android-sdk-windows\extras\m2repository\com\mycompany\myproduct\mylib\mylib-release\1.0\mylib-release-1.0.aar, together with a handmade POM file.

POM file:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>1.0</modelVersion>
	<groupId>com.mycompany.myproduct.mylib</groupId>
	<artifactId>mylib-release</artifactId>
	<version>1.0</version>
	<packaging>aar</packaging>
</project>

I also added the following lines into gradle.properties file in the Android Studio:

    POM_NAME=mylib-release
    POM_ARTIFACT_ID=mylib-release
    POM_PACKAGING=aar

Although I am not sure are these necessary or not.

It seems that this way the file is included into the build. At least the Unreal launch complained over wrong paths until I got them right.

To get forward I should now make AndroidThunkJava_* methods for all my library public static methods, I think.

Should I make a class similar to AndroidJNI.*, except for my own library only? Or should I add my own library methods into AndroidJNI class? And where to put these files (in same place as AndroidJNI or somewhere in my own project structure)?

If by “simple” you mean without playing with C++ then no, you need to brige code from Java to C++ in order to allow UE4 to use it, or else you use already existing APIs and only modify java portion, but i don’t of any library that this would be sufficient.

The Java portion of UE4 is inside /Build/Android you can extend or modify that code by placing extra stuff in your project in that location, so you add aar library same way as you would do in any other andorid project. But thats not enough, in order for that library to be usable in UE4 you need to bind it in C++ and potentially create some blueprint nodes if you want to. For that you need to create extra classes in C++ (you can do that in project or plugin, where no need to modify engine source code), to check how to do it look up AndroidJNI in engine source code:

https://github.com/EpicGames/UnrealEngine/tree/f509bb2d6c62806882d9a10476f3654cf1ee0634/Engine/Source/Runtime/Launch/Public/Android

Thanks you for your answer! By “simple” I meant without modifying the engine code. Wishful thinking, I am afraid :).

Could you open this a bit “so you add aar library same way as you would do in any other android project”. This is my first UE Android project, so I have no idea how it is normally done. What folder shall I drop the .aar, which build files to modify to get it included?

I updated the question with current status. I think I got the library in the build now.

As you see you dont need to modify :slight_smile: just add files to java directory. by Android i mean project without UE4, Android applicaiton in general since aar fiels is android features

Just add class in wither plugin or game project code and do what AndroidJNI does

I am still not sure where I shall place the new MyLibJNI.cpp and MyLibJNI.h files (MyLibJNI is my new JNI interface c++ class with AndroidThunkJava calls).

Somewhere in my Unreal project folders?

Or the same place as AndroidJNI.* classes are, i.e. C:\UE42\UnrealEngine\Engine\Source\Runtime\Launch\Public\Android\AndroidJNI.h ?

The best thing to do here would be to make a plugin that uses a UPL xml file to add the AAR dependency and to contain your functions instead of placing your code into the engine itself. Or, if this will just be for your C++ project you can just place the code there.

	<buildGradleAdditions>
		<insert>
dependencies {
	implementation('com.mycompany.myproduct.mylib:mylib-release:1.0')
}
		</insert>
	</buildGradleAdditions>

To use the AndroidJNI code in your code you just need to put in a private dependency on Launch module to have the include paths available: You can do this in the Build.cs like this:

PrivateDependencyModulesNames.Add("Launch");

Then you’d want to include this in your code that uses JNI:

#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"

Then if you had a Java function like this:

void AndroidThunkJava_MyCode_SetIntegerKey(String Key, int Value)

you could call it like this:

void FMyModule::SetIntegerKey(const FString& Key, int32 Value)
{
	if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
	{
		static jmethodID SetIntegerKeyMethod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_MyCode_SetIntegerKey", "(Ljava/lang/String;I)V", false);

		jstring KeyJava = Env->NewStringUTF(TCHAR_TO_UTF8(*Key));
		FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, SetIntegerKeyMethod, KeyJava, Value);
		Env->DeleteLocalRef(KeyJava);
	}
}

I made a plugin named AndroidLib. I did not find any suitable UPL example, but I hacked together this from some old APLs and UPLs that I found:

<?xml version="1.0" encoding="utf-8"?>
<root xmlns:android="http://schemas.android.com/apk/res/android">
	<trace enable="true"/>
	<init>
		<log text="AndroidLib init"/>		
	</init>

  <AARImports>
    <insertValue value="repository $S(PluginDir)/../../ThirdParty/repository"/>
    <insertNewline/>
        <insert>com.MyCompany.MyProduct.MyLib,MyLib-release,1.0</insert>
    <insertNewline/>
  </AARImports>
  
  <buildGradleAdditions>
    <insert>
      dependencies {
        implementation('com.MyCompany.MyProduct.MyLib:MyLib-release:1.0')
      }
    </insert>
  </buildGradleAdditions>

  <!-- optional additions to the GameActivity imports in GameActivity.java -->
  <gameActivityImportAdditions>
    <insert>
    </insert>
  </gameActivityImportAdditions>

  <!-- optional additions to proguard -->
  <proguardAdditions>
    <insert>
      <![CDATA[
      -keepattributes Signature
      -dontskipnonpubliclibraryclassmembers

     -keepclassmembers class com.epicgames.ue4.GameActivity {
            public <methods>;
            public <fields>;
     }
    ]]>
    </insert>
  </proguardAdditions>

Does this UPL seem correct?

I dropped MyLib-release.aar into the path:

E:/Unreal Projects/MyProject/Plugins/AndroidLib/ThirdParty/repository/MyLib-release.aar

I also added PrivateDependencyModulesNames.Add(“Launch”); into MyProject.Build.cs file

Could not test the actual connection today, launching to device takes so long I have to run :frowning:

This resulted into following error when Launched into device

LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : Failed to find Method
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : [2018.10.12-07.15.29:005][  0]Assertion failed: Method != 0 [File:E:\UE42\UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\AndroidJNI.cpp] [Line: 203]
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : Failed to find Method
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : LogAndroid: Error: === Critical error: ===
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : [2018.10.12-07.15.29:005][  0]LogAndroid: Error: === Critical error: ===
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : LogAndroid: Error:
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : [2018.10.12-07.15.29:005][  0]LogAndroid: Error:
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : LogAndroid: Error: Assertion failed: Method != 0 [File:E:\UE42\UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\AndroidJNI.cpp] [Line: 203]
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : [2018.10.12-07.15.29:005][  0]LogAndroid: Error: Assertion failed: Method != 0 [File:E:\UE42\UnrealEngine\Engine\Source\Runtime\Launch\Private\Android\AndroidJNI.cpp] [Line: 203]
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : LogAndroid: Error: Failed to find Method
LogPlayLevel:   10-12 10:15:29.005 18659 18694 D UE4     : [2018.10.12-07.15.29:005][  0]LogAndroid: Error: Failed to find Method

Obviously FindMethod does not find the method. But why?

To test the lib connection, I created an Actor based class “AThirdActor”, intantiated that in scene, and placed a call into BeginPlay() to

public static void createLibManagerInstance()

method in the .aar library

header:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"

#include "ThirdActor.generated.h"

UCLASS()
class MYLIBUNREALEXAMPLE_API AThirdActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AThirdActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	void CreateLibInstance();

};

cpp:

#include "ThirdActor.h"

#ifdef USE_ANDROID_JNI
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
#endif

AThirdActor::AThirdActor()
{
	PrimaryActorTick.bCanEverTick = true;

}

void AThirdActor::BeginPlay()
{
	Super::BeginPlay();
	
	CreateLibInstance();
}

void AThirdActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void AThirdActor::CreateLibInstance()
{
#ifdef USE_ANDROID_JNI
	if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
	{
		static jmethodID CreateLibManagerInstanceMethod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "createLibManagerInstance", "()V", false);
		FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis, CreateLibManagerInstanceMethod);
	}
#endif
}

Looking at the proposed codeline

static jmethodID SetIntegerKeyMethod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_MyCode_SetIntegerKey", "(Ljava/lang/String;I)V", false);

It seems that it is looking the method from FJavaWrapper::GameActivityClassID class, which seems to be the GameActivity.java class. So should I add something there as well?

I think my question is that should I add features to GameActivity class, namely including my .aar calls there, OR can I access MyLibClass (in the .aar) and its methods directly in the AndrodJNI, like this

    static jmethodID SetIntegerKeyMethod = FJavaWrapper::FindMethod(Env, FJavaWrapper::MyLibClassID, "AndroidThunkJava_MyCode_SetIntegerKey", "(Ljava/lang/String;I)V", false);    

In AndroidJNI there is this kind of calls

jclass localGameActivityClass = FindClass(Env, "com/epicgames/ue4/GameActivity", bIsOptional);

Can this FindClass be used to find MyLibClass in the .aar?

I made another test, trying to import the com.mycompany.myproduct.mylib.MyLib class into GameActivity.java.template, and added the “thunk methods” there, but this produces the following error

LogPlayLevel: :permission_library:compileDebugJavaWithJavac
LogPlayLevel: :permission_library:transformClassesAndResourcesWithPrepareIntermediateJarsForDebug
LogPlayLevel: :app:javaPreCompileDebug
LogPlayLevel: :app:compileDebugJavaWithJavacZ:\app\src\main\java\com\epicgames\ue4\GameActivity.java:120: error: package com.mycompany.myproduct.mylib does not exist
LogPlayLevel: import com.mycompany.myproduct.mylib.MyLib;
LogPlayLevel:                            ^
LogPlayLevel: Z:\app\src\main\java\com\epicgames\ue4\GameActivity.java:2700: error: cannot find symbol
LogPlayLevel:         MyLib.createLibManagerInstance();
LogPlayLevel:         ^
LogPlayLevel:   symbol:   variable MyLib
LogPlayLevel:   location: class GameActivity

This seems like my UPL does not work properly, as the library is not found.

UPL is fine; it is likely an issue with the embedded package path in the AAR not resolving.

If you want to skip GameActivity, you can just find your method using your own class. You will need to make a GlobalRef or the method will not be stable (would have to do this on every call):

Do this on initializing and store globalMyClass:

JNIEnv* Env = FAndroidApplication::GetJavaEnv()
jclass localMyClass = FindClass(Env, "com/mycompany/myproduct/mylib", false);
jclass globalMyClass = (jclass)Env->NewGlobalRef(localMyClass);
Env->DeleteLocalRef(localMyClass);

Tried that, and it resulted into this

    : Assertion failed: Class != 0 [File:E:/UE42/UnrealEngine/Engine/Source/Runtime/Launch/Private/Android/AndroidJNI.cpp] [Line: 197]
    : Failed to find Class
    : [2018.10.15-07.04.10:830][  0]Assertion failed: Class != 0 [File:E:/UE42/UnrealEngine/Engine/Source/Runtime/Launch/Private/Android/AndroidJNI.cpp] [Line: 197]
    : Failed to find Class
    : LogAndroid: Error: === Critical error: ===
: [2018.10.15-07.04.10:830][  0]LogAndroid: Error: === Critical error: ===
: LogAndroid: Error:
: [2018.10.15-07.04.10:830][  0]LogAndroid: Error:
: LogAndroid: Error: Assertion failed: Class != 0 [File:E:/UE42/UnrealEngine/Engine/Source/Runtime/Launch/Private/Android/AndroidJNI.cpp] [Line: 197]
: [2018.10.15-07.04.10:830][  0]LogAndroid: Error: Assertion failed: Class != 0 [File:E:/UE42/UnrealEngine/Engine/Source/Runtime/Launch/Private/Android/AndroidJNI.cpp] [Line: 197]
: LogAndroid: Error: Failed to find Class
: [2018.10.15-07.04.10:830][  0]LogAndroid: Error: Failed to find Class
: LogAndroid: Error: [Callstack] 0x00000000C36BF5A8 (0x000000000301F5A8) libUE4.so!StaticFailDebug(char16_t const*, char const*, int, char16_t const*, bool)  []
...
: LogAndroid: Error: [Callstack] 0x00000000C2E4D298 (0x00000000027AD298) libUE4.so!FJavaWrapper::FindClass(_JNIEnv*, char const*, bool)  []
...

I have a feeling that something is wrong in the config/paths/gradle/manifest/… but have no clue what!?

In ADB I get the following error msg:

: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
: Abort message: 'java_vm_ext.cc:534] JNI DETECTED ERROR IN APPLICATION: JNI CallVoidMethodV called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.mycompany.myproduct.mylib.MyLibClass" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib, /system/vendor/lib, /system/lib, /system/vendor/lib]]'

I created a minimal Unreal project with an aar plugin and pushed it into git. It is obviously a not-yet-working version. If someone finds a cause for crashing, please let me know.

I solved this by simply pulling the .jar out of the .aar package. .jar works without problems, and luckily I did not need any resources from .aar.

How did you include the .jar in the project? Through the build.cs or other? Can you post exactly what you did?

Thank you.

Can you use the buildGradleAdditions to add remote dependencies?

Would you be able to add the repositories the same way (how)?

Is the you used placed in the .xml file that is included in the build.cs?

If it is not remote where would you put the .aar and .jar files so they would be included? Can you define where they are?