Migrate from NativeActivity Part of Android Game Development Kit.
This page describes how to migrate from
NativeActivity
to
GameActivity
in your Android game project.
GameActivity
is based on NativeActivity
from the Android
framework, with enhancements and new features:
- Supports
Fragment
from Jetpack. - Adds
TextInput
support to facilitate soft keyboard integration. - Handles touch and key events in the
GameActivity
Java class rather than theNativeActivity
onInputEvent
interface.
Before migrating, we recommend reading the
get started guide, which describes how
to set up and integrate GameActivity
in your project.
Java build script updates
GameActivity
is distributed as a
Jetpack library. Make sure to apply the Gradle script updating steps described
in the get started guide:
Enable Jetpack library in your project’s
gradle.properties
file:android.useAndroidX=true
Optionally, specify a Prefab version, in the same
gradle.properties
file, for example:android.prefabVersion=2.0.0
Enable Prefab feature in your app’s
build.gradle
file:android { ... // other configurations buildFeatures.prefab true }
Add the
GameActivity
dependency to your application:- Add the
core
andgames-activity
libraries. - If your current minimum supported API level is less than 16, update it to at least 16.
- Update the compiled SDK version to the one that the
games-activity
library requires. Jetpack typically requires the latest SDK version at the release build time.
Your updated
build.gradle
file might look something like this:android { compiledSdkVersion 33 ... // other configurations. defaultConfig { minSdkVersion 16 } ... // other configurations. buildFeatures.prefab true } dependencies { implementation 'androidx.core:core:1.9.0' implementation 'androidx.games:games-activity:1.2.2' }
- Add the
Kotlin or Java code updates
NativeActivity
can be used as a startup activity and creates a full screen
application. At present, GameActivity cannot be used as the startup
activity. Apps must derive a class from GameActivity
and use that as
the startup activity. You must also make additional configuration changes to
create a full screen app.
The following steps assume your application uses NativeActivity
as the startup
activity. If that is not the case, you can skip most of them.
Create a Kotlin or Java file to host the new startup activity. For example, the following code creates the
MainActivity
as the startup activity and loads the application’s main native library,libAndroidGame.so
:Kotlin
class MainActivity : GameActivity() { override fun onResume() { super.onResume() // Use the function recommended from the following page: // https://d.android.com/training/system-ui/immersive hideSystemBars() } companion object { init { System.loadLibrary("AndroidGame") } } }
Java
public class MainActivity extends GameActivity { protected void onResume() { super.onResume(); // Use the function recommended from // https://d.android.com/training/system-ui/immersive hideSystemBars(); } static { System.loadLibrary("AndroidGame"); } }
Create a full screen app theme in the
res\values\themes.xml
file:<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Application.Fullscreen" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowFullscreen">true</item> <item name="android:windowContentOverlay">@null</item>" </style> </resources>
Apply the theme to the application in the
AndroidManifest.xml
file:<application android:theme=”@style/Application.Fullscreen”> <!-- other configurations not listed here. --> </application>
For detailed instructions for full screen mode, see to the immersive guide and example implementation in the games-samples repo.
This migration guide does not change the native library name. If you do change it, ensure the native library names are consistent in the following three locations:
Kotlin or Java code:
System.loadLibrary(“AndroidGame”)
AndroidManifest.xml
:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />
Inside the C/C++ build script file, for example
CMakeLists.txt
:add_library(AndroidGame ...)
C/C++ build script updates
The instructions in this section use cmake
as the example. If your application
uses ndk-build
, you need to map them to the equivalent commands described in
ndk-build documentation page.
GameActivity’s C/C++ implementation has been providing a source code release. For version 1.2.2 a later, a static library release is provided. The static library is the recommended release type.
The release is packed inside the AAR with the
prefab
utility. The native code includes GameActivity’s C/C++ sources and the
native_app_glue
code. They need to be built together with your
application's C/C++ code.
NativeActivity
applications already use the native_app_glue
code shipped inside NDK. You must replace it with GameActivity’s version
of native_app_glue
. Other than that, all cmake
steps documented inside
the getting started guide apply:
Import either the C/C++ static library or the C/++ source code into your project as follows.
Static library
In your project's
CMakeLists.txt
file, import thegame-activity
static library into thegame-activity_static
prefab module:find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)
Source code
In your project's
CMakeLists.txt
file, import thegame-activity
package and add it to your target. Thegame-activity
package requireslibandroid.so
, so if it's missing, you must also import it.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)
Remove all references to NDK’s
native_app_glue
code, such as:${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ... set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
If you are using the source code release, include the
GameActivity
source files. Otherwise, skip this step.get_target_property(game-activity-include game-activity::game-activity INTERFACE_INCLUDE_DIRECTORIES) add_library(${PROJECT_NAME} SHARED main.cpp ${game-activity-include}/game-activity/native_app_glue/android_native_app_glue.c ${game-activity-include}/game-activity/GameActivity.cpp ${game-activity-include}/game-text-input/gametextinput.cpp)
Work around the UnsatisfiedLinkError issue
If you encounter an UnsatsifiedLinkError
for the
com.google.androidgamesdk.GameActivity.initializeNativeCode()
function, add
this code to your CMakeLists.txt
file:
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
C/C++ source code updates
Follow these steps to replace NativeActivity
references in your
application with GameActivity
:
Use the
native_app_glue
released withGameActivity
. Search and replace allandroid_native_app_glue.h
usage with:#include <game-activity/native_app_glue/android_native_app_glue.h>
Set both motion event filter and key event filter to
NULL
so your app can receive input events from all input devices. You typically do this insideandroid_main()
function:void android_main(android_app* app) { ... // other init code. android_app_set_key_event_filter(app, NULL); android_app_set_motion_event_filter(app, NULL); ... // additional init code, and game loop code. }
Remove
AInputEvent
related code, and replace it with GameActivity’sInputBuffer
implementation:while (true) { // Read all pending events. int events; struct android_poll_source* source; // If not animating, block forever waiting for events. // If animating, loop until all events are read, then continue // to draw the next frame of animation. while ((ALooper_pollAll(engine.animating ? 0 : -1, nullptr, &events, (void**)&source)) >= 0) { // Process this app cycle or inset change event. if (source) { source->process(source->app, source); } ... // Other processing. // Check if app is exiting. if (state->destroyRequested) { engine_term_display(&engine); return; } } // Process input events if there are any. engine_handle_input(state); if (engine.animating) { // Draw a game frame. } } // Implement input event handling function. static int32_t engine_handle_input(struct android_app* app) { auto* engine = (struct engine*)app->userData; auto ib = android_app_swap_input_buffers(app); if (ib && ib->motionEventsCount) { for (int i = 0; i < ib->motionEventsCount; i++) { auto *event = &ib->motionEvents[i]; int32_t ptrIdx = 0; switch (event->action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: // Retrieve the index for the starting and the ending of any secondary pointers ptrIdx = (event->action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_UP: engine->state.x = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_X); engine->state.y = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_Y); break; case AMOTION_EVENT_ACTION_MOVE: // Process the move action: the new coordinates for all active touch pointers // are inside the event->pointers[]. Compare with our internally saved // coordinates to find out which pointers are actually moved. Note that there is // no index embedded inside event->action for AMOTION_EVENT_ACTION_MOVE (there // might be multiple pointers moved at the same time). ... break; } } android_app_clear_motion_events(ib); } // Process the KeyEvent in a similar way. ... return 0; }
Review and update logic that attached to NativeActivity’s
AInputEvent
. As shown in the previous step, GameActivity’sInputBuffer
processing is outside theALooper_pollAll()
loop.Replace
android_app::activity->clazz
usage withandroid_app:: activity->javaGameActivity
. GameActivity renames the JavaGameActivity
instance.
Additional steps
The previous steps cover NativeActivity's functionality, but GameActivity
has
additional features that you might want to use:
- TextInput.
- Game Controller.
- Fragment.
- New window InSets commands defined in NativeAppGlueAppCmd.
We recommend exploring these features and adopting them as appropriate for your games.
If you have any questions or recommendations for GameActivity or other AGDK libraries, create a bug to let us know.