This article is simultaneously published on my wechat official account. You can follow it by scanning the QR code at the bottom of the article or searching HelloWorld Jieshao on wechat.

preface

When using UE to develop mobile games, we inevitably have to interact with Android and iOS data. In this case, we need to add some operations in the code, so that C++ can call Java on the Android platform, Java can call C++; The same is true on iOS, C++ can call OC, OC can call C++. Since LAST time I have realized the mutual call between UE and iOS, the article portal, today I will continue to talk about the mutual call between UE and Android.

Android Environment construction

Install Android Studio based on the UE engine version you have installed. My engine version is 4.25.4, so I installed Android Studio version 3.5.3 as instructed by the UE documentation.

Once installed, take a look at the NDK version and check “Show Package Detail” in the lower right corner.

If the NDK is not installed in the specified version, select the correct version before downloading and installing the NDK.

Build the first UE project

After the Android environment is configured, we start to build a UE project, the main idea is to add a button on the interface UI, click the button to trigger an event, this part I have sorted out in the previous tutorial, you can click to view.

Android Packaging Configuration

Next, configure Android packaging in UE, find the Package option in Project Settings, and set it to Develop.

If you need a Release package, set it to “Release” in the drop-down list.

Go to “Platform” in “Project Settings” and configure “Android”, set both fields to Agree, accept the SDK certificate, and fill in the Android package name.

Fill in the path in the Android SDK.

PS: Since I am using a Mac, the path configuration is different from Windows.

The paths for the Android SDK and NDK can be viewed in Android Studio.

Find the path corresponding to the NDK based on the Android SDK path found in Android Studio.

And then you’re ready to pack.

Upgrade to AndroidX

After you have finished packing in UE, use Android Studio to open your UE project directory: Intermediate -> Android -> armv7 -> gradle

Add the following two configurations to the global gradle.properties:

android.useAndroidX=true
android.enableJetifier=true
Copy the code

Upgrade your project to AndroidX using the upgrade features that come with Android Studio.

Then execute Gradle Sync to compile the whole project. If the project is successfully compiled, it means that our project has been upgraded to AndroidX.

Android AAR

When developing Android using UE4, you often need to access third-party libraries, so let’s do a simple example.

In the previous section, I loaded UE bundled Gradle into Android Studio, and then File -> New Module to create a New AAR library.

I’ll tentatively name this library LoginSDK and have the following directory structure:

At this point, a simple third-party library is created. In the following articles, I will continue to teach you how to call this third-party library.

C + + calling Java

In UE how to call Java functions through C++, this time needs to use JNI call to achieve. Above, the UE project we created has implemented a button click event in which Java functions can be called.

So where should we write our Java function?

UE provides a gameActivity. Java class with the Android package, which allows UE to call Java code.

So in gameActivity.java, we add a function public void AndroidThunkJava_InitName() that does the following: AndroidThunkJava_InitName()

public void AndroidThunkJava_InitName(a)
{
    Log.debug("AndroidThunkJava_InitName");
}
Copy the code

To call Java through C++, you first need to know the signature of the Java function to be called. I won’t go into this knowledge here. Go back to the button click event in our C++ code and add the following code.

void UMyUserWidget::callLoginFunction(a)
{
#if PLATFORM_ANDROID
    if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
    {
        jmethodID GetPackageNameMethodID = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_InitName"."()V;".false);
        FJavaWrapper::CallObjectMethod(Env, FJavaWrapper::GameActivityThis,GetPackageNameMethodID);
    }
#endif
}
Copy the code

Since our C++ code has been modified, we need to repackage the Android project. Once packaged, run on our Android Studio, click the button, and the corresponding log will be printed in the console.

C++ call Java code has been successfully implemented, but in the above article, we have not finished the content of a new third-party library, that will continue to talk about how to call android third-party library function methods.

First, implement some logic in the LoginSDK library created above:

  1. Create a LoginActivity and add EditText and Button controls
  2. Jump to LoginActivity from GameActivity
  3. After clicking the button in the LoginActivity, the value in the EditText control is called back to the GameActivity

Call LoginSDK from AndroidThunkJava_InitName() in gameActivity.java.

public void AndroidThunkJava_InitGame(a)
	{
		Log.debug("AndroidThunkJava_InitGame");

		// call loginsdk
		Intent intent = new Intent(this, LoginActivity.class);

		String message = "UE4";
		intent.putExtra("com.example.MESSAGE", message);
		startActivityForResult(intent, 998);
	}
Copy the code

Implement the following code in LoginActivity:

public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        Intent intent = getIntent();
        String message = intent.getStringExtra("com.example.MESSAGE");
        Log.d("message", message);
    }

    public void sendMessage(View view) {
        EditText editText = (EditText) findViewById(R.id.editText);
        String value = editText.getText().toString();

        // to C++
        Intent intent = new Intent();
        intent.putExtra("LOGIN", value);
        setResult(998, intent); finish(); }}Copy the code

AndroidThunkJava_InitGame sets the RequestCode of startActivityForResult(Intent, 998) to 998. Protected void onActivityResult(int requestCode, int resultCode, Intent data) The code is as follows:

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		if( requestCode == DOWNLOAD_ACTIVITY_ID)
		{
            // ...
		}
		else if (requestCode == 998){
			android.util.Log.d("test"."Received data from LoginSDK");
			String result = data.getStringExtra("LOGIN");
			android.util.Log.d("test", result);
			//nativeOnLoginCallBack(result);
		}
		else if( IapStoreHelper ! =null )
		{
			super.onActivityResult(requestCode, resultCode, data);
		}
		else
		{
			super.onActivityResult(requestCode, resultCode, data);
		}

		if(InitCompletedOK)
		{
			nativeOnActivityResult(this, requestCode, resultCode, data); }}Copy the code

Ps: RequestCode values you can customize

Java call c + +

UE also declared many native functions in the GameActivity generated for our game, such as:

public native int nativeGetCPUFamily(a);
	public native boolean nativeSupportsNEON(a);
	public native void nativeSetAffinityInfo(boolean bEnableAffinity, int bigCoreMask, int littleCoreMask);
	public native void nativeSetConfigRulesVariables(String[] KeyValuePairs);

	public native boolean nativeIsShippingBuild(a);
	public native void nativeSetAndroidStartupState(boolean bDebuggerAttached);
	public native void nativeSetGlobalActivity(boolean bUseExternalFilesDir, boolean bPublicLogFiles, String internalFilePath, String externalFilePath, boolean bOBBInAPK, String APKPath);
	public native void nativeSetObbFilePaths(String OBBMainFilePath, String OBBPatchFilePath);
	public native void nativeSetWindowInfo(boolean bIsPortrait, int DepthBufferPreference);
	public native void nativeSetObbInfo(String ProjectName, String PackageName, int Version, int PatchVersion, String AppType);
	public native void nativeSetAndroidVersionInformation( String AndroidVersion, String PhoneMake, String PhoneModel, String PhoneBuildNumber, String OSLanguage );

	public native void nativeSetSurfaceViewInfo(int width, int height);
	public native void nativeSetSafezoneInfo(boolean bIsPortrait, float left, float top, float right, float bottom);

	public native void nativeConsoleCommand(String commandString);
	public native void nativeVirtualKeyboardChanged(String contents);
	public native void nativeVirtualKeyboardResult(boolean update, String contents);
	public native void nativeVirtualKeyboardSendKey(int keyCode);
	public native void nativeVirtualKeyboardSendTextSelection(String contents, int selStart, int selEnd);
	public native void nativeVirtualKeyboardSendSelection(int selStart, int selEnd);

	public native void nativeInitHMDs(a);

	public native void nativeResumeMainInit(a);

	public native void nativeOnActivityResult(GameActivity activity, int requestCode, int resultCode, Intent data);

	public native void nativeGoogleClientConnectCompleted(boolean bSuccess, String accessToken);

	public native void nativeVirtualKeyboardShown(int left, int top, int right, int bottom);
	public native void nativeVirtualKeyboardVisible(boolean bShown);

	public native void nativeOnConfigurationChanged(boolean bPortrait);

	public native void nativeOnInitialDownloadStarted(a);
	public native void nativeOnInitialDownloadCompleted(a);
Copy the code

These functions are implemented in C++ and are automatically called into the engine’s C++ code when executed in Java. You can add custom native functions to GameActivity yourself.

Here’s a simple example of adding a native function to GameActivity and implementing it on the C++ side.

public native void nativeOnLoginCallBack(String msg);
Copy the code

Implement a function like this in the C++ code of the MyUserWidget class:

#if PLATFORM_ANDROID
JNI_METHOD void Java_com_epicgames_ue4_GameActivity_nativeOnLoginCallBack(JNIEnv* jenv, jobject thiz, jstring msg)
{
    FString message;
    message = FJavaHelper::FStringFromParam(jenv, msg);
    UE_LOG(LogTemp, Log, TEXT("Java_com_epicgames_ue4_GameActivity_nativeOnLoginCallBack=[%s]"), *message);
}
#endif
Copy the code

Ps: PLATFORM_ANDROID macros are indispensable

Ue4 is the package name of ue-generated gameActivity.java (package com.epicgames.ue4; .

As you can see, JNIMETHOD names are implemented in C++ according to the following rules:

RType Java_PACKAGENAME_CLASSNAME_FUNCNAME(JNIEnv*,jobject thiz,Oher...)

Copy the code

Note: this implementation function can be placed in any C++

Then, we can execute the C++ logic on the Java side. After I receive the LoginActivity callback in GameActivity, Public native void nativeOnLoginCallBack(String MSG);

The code is as follows:

if (requestCode == 998){
			android.util.Log.d("test"."Received data from LoginSDK");
			String result = data.getStringExtra("LOGIN");
			android.util.Log.d("test", result);
			nativeOnLoginCallBack(result);
		}
Copy the code

From the printed log, you can see that Java has successfully called C++.

So at this point, the whole process of calling is over.

conclusion

Finally, summarize the points we need to pay attention to in the whole development process:

  • Android Studio version
  • SDK path configuration in UE editor
  • Package: The Gradle folder is reset after each package. Remember to save Gradle as after the first package and then replace the resources and so library
  • C++ to Java: JNI, GameActivity.java
  • Java to C++: native functions, JNIMETHOD, PLATFORM_ANDROID macro

I am Jie Shao, if you think my writing is good, then please give me a thumbs-up + comments + favorites before leaving oh!

Previous articles:

  • UE4 Development Pit Avoidance Guide (continuously updated)
  • It’s time to start the New Year. Let’s celebrate with a little fireworks
  • Love and Hate with Apple auditors
  • Love and Hate with Apple auditors (Part 1)
  • A common tool of 2021 | 2021 year-end summary
  • Binary tree brush summary: binary search tree properties
  • Binary tree summary: binary tree properties
  • Binary tree summary: binary tree modification and construction
  • StoreKit2 smells this good? Yeah, I tried it. It smells good
  • After reading this article, I am no longer afraid of being asked how to construct a binary tree.
  • The game guys are trying to get people to pay again. That’s bad!
  • Take you rolled a netease holding cloud music home | adapter
  • NetEase Cloud Music Home Page (3)
  • NetEase Cloud Music Home Page (2)
  • NetEase Cloud Music Home Page (a)
  • Does the code need comments? Write and you lose
  • I would not study in Codable for a long time. They are for fun
  • IOS handles web data gracefully. Do you really? Why don’t you read this one
  • UICollectionView custom layout! This one is enough

Please drink a cup ☕️ + attention oh ~

  1. After reading, remember to give me a thumbs-up oh, there is 👍 power
  2. Follow the public number – HelloWorld Jie Shao, the first time push new posture

Finally, creation is not easy, if it is helpful to you, I hope you can praise and support, what questions can also be discussed in the comments section 😄 ~ **