Android Java Libraries in UE4 Game (OUYA SDK, Google Play Game Services, etc.)

I write my Java in the Java/src subfolder.
https://github.com/tgraupmann/UnrealEngine/tree/4.7-OUYA/Engine/Build/Android/Java

I see, but I’d really like to not interfere with the Engine files. I’d rather change project files, so maybe putting a .jar file there will work, like you wrote before. But will JNI see the class if there will be no reference to it from GameActivity…?

You don’t need a reference from the Activity file to be able to find the class with JNI.

It has been at least a year from when the old build system in Eclipse is abandoned for the newer build system in Android Studio. It hinges on build.gradle, which adds an extra layer to the build tool chain before AAPT that does all of the merging for you that you have to do manually when building using ANT without build.gradle. Adding multiple JAR or AAR to an APK becomes almost trivial by comparison between the two tool chains. The latest version of Eclipse may even support this but I haven’t tried using it since the build tool chain overhaul in Android Studio (also I hate Eclipse almost as much as I hate MFC). Link for documentation of the overhauled build system

I work at Pebblebee, we share some AAR files publicly, it would be great to eventually be able to integrate those directly instead of the current workaround. We cannot share our solution with the community currently as it requires pulling in the private code directly

The Unreal Engine toolchain setup pulls the installer for NVPACK 1R1. Don’t use that, it is so frigin’ old. You can install 1R4 here which is the latest and it still works fine with Unreal Engine Download Center | NVIDIA Developer

Now I know it says NVIDIA CodeWorks but it is the same thing, it still installs the toolchain in NVPACK just like before. And you can go in to the ANT ~/NVPACK/android-sdk-macosx/tools/ant/build.xml file and change the default version of Java from 1.5 to 1.7 (line 71, 72) so you don’t get that deprecated compiler version warning regurgitated constantly in the Unreal Engine build output. The build output will still tell you to ignore warnings about compiling with an old version of java, but now you actually won’t see that warning anywhere in the output. Which is the way it should be.

<property name=“java.target” value=“1.7” />
<property name=“java.source” value=“1.7” />

I also manually upgraded Apache-Ant to 1.9.6 and gradle to 2.12 since the versions installed by 1R4 were still not the latest. Make sure you force the verbosity to help track down errors (~/NVPACK/android-sdk-macosx/tools/ant/build.xml, line 88), though if the failure happens inside of AAPT then you are basically screwed since it seems to be mostly a black box. Unless you do a custom build of AAPT. Though if anyone has been able to get debug output from AAPT within the Unreal Engine build toolchain then let me know.

<property name=“verbose” value=“true” />

In your working copy of the source tree under /Engine/Build/Android/Java/libs/ I added android-support-v7-appcompat.jar and android-support-v7-recyclerview.jar which are standard android support libraries that you should expect to be a dependency of any recently written core common library for android java.

You can create a file /Engine/Build/Android/Java/ant.properties and in it you list off all of the root source trees for com, for example the content of my file is

source.dir=src; /Users/MyUserName/git/pb-android-lib-common/lib/src/main/java; /Users/MyUserName/git/pb-android-lib-bluetooth/lib/src/main/java

but this will miss the AndroidManifest.xml and res folder for everything except the first folder, so you have to manually merge the manifest file and resource folder for every other source tree, and you also have to go in and manually replace the import namespace for ‘BuildConfig’ and ‘R’ for every code file under those other folders that are importing BuildConfig or R (or change your project’s package path to be the same as the internal code for the package you are importing. And everywhere in the toolchain where it had an explicit version of Android to compile or build against I updated them to the version of Android on the phone (in project.properties and the manifest for example), which in my case is 22 (since my phone has 5.1.1 installed). You can update AndroidManifest.xml indirectly through the project settings (if you want to go the direct route it also gives directions in the project settings for where to place a file directly to be picked up by the build system).

I also added local.properties set to my install path for the android sdk

sdk.dir=/Users/MyUserName/NVPACK/android-sdk-macosx

(But 1R4 is still old, using 1R4’s internal update mechanism we still get older stuff compared to Android Studio’s Android SDK update mechanism, so just be warned about that. I have 3 separate installs of Android SDK, one from NVPACK, one from Eclipse, one from Android Studio, so I went and manually pulled in the very latest from Android Studio and Eclipse into NVPACK but you probably don’t need to do that. I also pulled the full [Google] Android source tree and successfully did a full ARM32 build of AOSP … just in case.)

Thanks for all this detail. Gradle for the win. I plan on upgrading to AAR files and this info will come in handy.

Yep, thanks for the updated info!

Hey guys is there an official way to file bugs against Unreal Engine? I want to file a bug for build.gradle support. Or someone else who knows how to do it can do it for me and reference this post. Is opening a support ticket the standard process for opening a bug or is there a better way?

How will I figure out whether or not opening a support ticket is the correct way to file a bug? I will open a support ticket, and then ask the question in the support ticket. LOL!

You can fill bugs on https://answers.unrealengine.com setting the section of the question to “Bug Reports”.

Hlep! I put my JAR libraries in the UnrealEngine\Engine\Build\Android\Java\libs folder. But, when I write my code in GameActivity.java using the funtion of **.jar, it shows me errors. What Should I do to make the project recognize the JAR, now import command is unuseful!

Hlep! I put my JAR libraries in the UnrealEngine\Engine\Build\Android\Java\libs folder. But, when I write my code in GameActivity.java using the funtion of **.jar, it shows me errors. What Should I do to make the project recognize the JAR, now import command is unuseful!

I’ve added a pull request and example to show loading a JAR from a UE4 plugin.
https://github.com/EpicGames/UnrealEngine/pull/2313

The JNI_OnLoad event is a special event that gives you one chance to find certain classes. This PR adds a callback for your UE4 plugins to hook on to the JNI_OnLoad event so that you can register classes from your JAR.

Here’s the C++ plugin project:

Header:

Implementation:

And I was able to place the JAR here within the plugin.

This way, in order to add a JAR and hook onto the custom classes, it won’t require editing AndroidJNI.h/.cpp in the engine code.

This would also allow your JAR and plugin to work in the retail version of UE4 instead of compiling from source.

Furthermore, this means you could submit your UE4/Plugin/JAR to the marketplace as a product to make it accessible to everyone.

I’ve also submitted a PR for adding an Android input callback.
https://github.com/EpicGames/UnrealEngine/pull/2314

Here’s the C++ plugin project:

Header:

Implementation:

This relates to JAR handling because I add special controller remapping in the JAR and use this callback to pass Android input to a JAR, which gets remapped and then passed back to the UE4 plugin.

The result is that I can add support to the framework for new controllers and mappings without needing to rebuild/republish the UE4 games and apps.

Thanks for all this detail. Now, I want to use some functions of a JAR file in GameActivity.java. What should I do? What files need I to modify? please give me some suggestions. Thank you again.lib.pnglib.pnglib.png lib_dir.png src.png src_dir.png

You should just need to place your JAR file:
https://github.com/tgraupmann/UnrealEngine/tree/4.10-Cortex/Engine/Build/Android/Java/libs

And then you import your package right in the GameActivity.
https://github.com/tgraupmann/UnrealEngine/blob/4.10-Cortex/Engine/Build/Android/Java/src/com/epicgames/ue4/GameActivity.java#L77

Although that requires building UE4 from source and my previous example shows how to add your JAR from a UE4 plugin that would work in the retail version of UE4.

That is assuming they accept my changes. I submitted a PR against the 4.11 branch. Does anyone know if they prefer that or if I submit against the master branch instead?

Thanks!

Can I ask what functions are you calling in GameActivity.java and from what methods? I’m adding to the PR which adds a listener to expose more Java events to UE4 plugins.

Of course, I need to add a bunch of comments for each of these Activity methods.

You can see with these added callbacks, you’ll have access to invoke your JAR/Java methods from your UE4 plugin without modifying the engine’s GameActivity.java.

Here is the logcat showing the example log lines.

I have two outstanding PRs.

Added a callback for the JNI_OnLoad event to be used by UE4 plugins - https://github.com/EpicGames/UnrealEngine/pull/2313
Add Android Input callback - https://github.com/EpicGames/UnrealEngine/pull/2314

The result of all this is that I’ll be able to release a Cortex/OUYA plugin to the marketplace. So a game developer would just need to get an example project like Tappy Chicken and then add the Cortex plugin from the marketplace. The user builds for Android and the game would work with a controller on the Forge TV Android console. And that’s without touching any code.

Thank you very much! I have solved my problem. The problem is that I imported a wrong package. ;):wink:

I merged both PRs into a new branch while I wait for approval.
The new branch - https://github.com/tgraupmann/UnrealEngine/tree/4.11-Cortex

Now I’ll focus on rewriting the OUYA/Cortex plugin as a UE4 plugin instead of embedding the plugin into the UE4 engine source.
The old way - https://github.com/tgraupmann/UnrealEngine/tree/4.10-Cortex

I’ve created an Example Project that uses a UE4 plugin to load JAR/Java classes.

The example project referenced my plugin “OuyaSDKPlugin” in the build script.

I’ve created my UE4 plugin here:

I placed the JAR for the plugin here:

I placed the Java source for the plugin here:

I can build successfully, so that’s good. Now I just have to figure out how to get the plugin to initialize.

I need to figure out how to get the StartupModule() method in the UE4 plugin loading which should put something in the logcat.

I can see that the plugin is enabled using the menu item Edit->Plugins and slicking on Project I see the plugin has enabled checked.

The type of plugin that I’m creating is referred to as an “Installed Plugin” in the docs.

Is there anything special needed to get a UE4 Plugin to load in Android?

I’ve added logging in the UE4 Plugin StartupModule().

The UE4 editor log shows the message:
LogOuyaSDKPlugin: *** OuyaSDKPlugin has loaded. ***

But when the game loads on Android I’m not seeing the entries appear in the logcat.

Hi tgaupmann,

The Android Plugin Language is the proper way to do what you are trying to do here. The GameActivity.java can be patched by the plugin and copying of dependencies (assets, libs, jars) can be done with it on a conditional basis. Also, there is no need to hook into JNI_OnLoad. Here is an example from earlier GearVR.cpp:


// call out to JNI to see if the application was packaged for GearVR
bool AndroidThunkCpp_IsGearVRApplication()
{
	bool bIsGearVRApplication = false;
	if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
	{
		static jmethodID Method = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_IsGearVRApplication", "()Z", false);
		bIsGearVRApplication = FJavaWrapper::CallBooleanMethod(Env, FJavaWrapper::GameActivityThis, Method);
	}
	return bIsGearVRApplication;
}

In your OuyaSDKPlugin.Build.cs you will want to add:


			if (Target.Platform == UnrealTargetPlatform.Android)
			{
				string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, BuildConfiguration.RelativeEnginePath);
				AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(PluginPath, "OuyaSDKPlugin_APL.xml")));
			}

Then you can make an OuyaSDKPlugin_APL.xml in the same directory as the OuyaSDKPlugin.Build.cs. Look at GearVR_APL.xml in Engine/Plugin/Runtime/GearVR/Source/GearVR for an example. The top comment block in AndroidPluginLanguage.cs documents the XML-based language.

As for the loading issue, try adding Android to the white-list in the .uplugin (take a look at GearVR.uplugin).

Thanks for the hints. I’ll take a look!

https://github.com/EpicGames/UnrealEngine/blob/4.11/Engine/Plugins/Runtime/GearVR/GearVR.uplugin

https://github.com/EpicGames/UnrealEngine/blob/4.11/Engine/Plugins/Runtime/GearVR/Source/GearVR/GearVR.Build.cs

https://github.com/EpicGames/UnrealEngine/blob/4.11/Engine/Plugins/Runtime/GearVR/Source/GearVR/GearVR_APL.xml

I’ll adapt to your suggestions. Thanks for the feedback!

With the whitelist option, I can see logging now!

I can see my void FOuyaSDKPlugin::StartupModule() is being invoked now!

It appears that the module is loaded after the JNI_OnLoad event and so FAndroidApplication::GetJavaEnv() will be the way to get the JNI environment after all.

I’ll incorporate that change next.

BOOM!

Looks like something failed to find the class from the JAR and Java.

Since I’m using the JNIEnv outside the JNI_OnLoad event, I think that’s the reason why the class lookup fails. You have one chance to look up non-system classes in that event.

Later, in another thread I could invoke methods on those classes with something like.

Looking at the design of the AndroidJNI.cpp I see you are doing GearVR lookups in that class. With a JNI_OnLoad event callback that would allow all the GearVR code to operate within it’s own plugin.

The class lookups fail whether I use JNIEnv directly or use the FJavaWrapper helper methods.

The class lookup does succeed if I used the callbacks from the game code.