-
Notifications
You must be signed in to change notification settings - Fork 0
[Android] JNI
CDATA - a section of element content that is marked for the parser to interpret as only character data, not markup.
Sentry - Can use as an example for JNI related stuff.
You can modify internal Java code with help of UPL by injecting the code in the places where $${TemplateName}$$
exists, for example gameActivityClassAdditions
in GameActivity.java.template
file.
If needed you can always wrap Android features into the preprocessor to cut it out from other platform builds:
#if PLATFORM_ANDROID
...
#endif
For example:
In <ModuleName>_UPL.xml
, for GameActivity.java.template
you can add:
<gameActivityClassAdditions>
<insert>
<![CDATA[
private static native void onLocalNotificationsPermissionResult(boolean isGranted);
]]>
</insert>
</gameActivityClassAdditions>
And in C++:
#include <jni.h>
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
...
extern "C" {
JNI_METHOD void Java_com_epicgames_unreal_GameActivity_onLocalNotificationsPermissionResult(JNIEnv* jenv, jobject self, jboolean bIsGranted)
{
if (bIsGranted == JNI_TRUE)
{
UE_LOG(LogTemp, Log, TEXT("Granted."));
}
else
{
UE_LOG(LogTemp, Log, TEXT("Denied."));
}
}
}
- The
Java_com_epicgames_unreal_GameActivity_
of the C++ function above reflects the path whereGameActivity.java
is located, it's important. -
<ModuleName>_UPL.xml
needs to be placed in the very top level directory of your Plugin/GameModule, where<ModuleName>.Build.cs
is placed.
First, add a new function into, for example, GameActivity.java (GameActivity.java.template):
<gameActivityClassAdditions>
<insert>
<![CDATA[
public boolean AndroidThunkJava_SomeTestJavaFunction(int number)
{
if(number == 69){
return true;
}
return false;
}
]]>
</insert>
</gameActivityClassAdditions>
Then fetch the said function and wrap it into something to easily execute it: .h
#include <jni.h>
#include "Android/AndroidJNI.h"
class FAndroidCustomHelperFunctions
{
public:
static void FindClassesAndMethods(JNIEnv* Env);
static bool AndroidThunkCpp_SomeTestJavaFunction(const int number);
private:
static jmethodID AndroidThunkJava_SomeTestJavaFunction;
}
.cpp
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
// For Env can potentially use `AndroidJavaEnv::GetJavaEnv();` or `FAndroidApplication::GetJavaEnv();`
void FAndroidCustomHelperFunctions::FindClassesAndMethods(){
JNIEnv* Env = AndroidJavaEnv::GetJavaEnv();
AndroidThunkJava_SomeTestJavaFunction = FJavaWrapper::FindMethod(
Env,
FJavaWrapper::GameActivityClassID, // Class to find method from
"AndroidThunkJava_SomeTestJavaFunction", "(I)Z", // Method name and its Signature
false /*unused arg*/
);
}
bool FAndroidCustomHelperFunctions::AndroidThunkCpp_SomeTestJavaFunction(const int number)
{
JNIEnv* Env = AndroidJavaEnv::GetJavaEnv();
bool result = FJavaWrapper::CallBooleanMethod(
Env,
FJavaWrapper::GameActivityThis, // Class to use
FAndroidCustomHelperFunctions::AndroidThunkJava_SomeTestJavaFunction, // Method to call
number // Args... (any amount)
);
return result;
}
UE 4 used code obfuscation system called ProGuard on it’s java layer, because of that functions name are modified and C++ references fails due to that name change. You need to add exception to your function in ProGuard configuration, you can find engine directory
\Engine\Build\Android\Java\proguard-project.txt
. All other linking function that engine used are already there too which shows you a example
<proguardAdditions>
<insert>
-dontwarn io.sentry.unreal.**
-keep class io.sentry.** { *; }
-keep interface io.sentry.** { *; }
</insert>
</proguardAdditions>