Introducing GameActivity for Android in 2023.1

Hello from the Android Platform team with some exciting news!

Starting from Unity 2023.1 you can now choose a new application entry for Android - GameActivity. Under the hood, this is powered by the GameActivity Jetpack library, with all the benefits that this brings.

Why switch to GameActivity?

We believe that this feature will improve your Android development and app runtime experience in three ways:

  • You will have better threading support for lifecycle, input, and touchscreen keyboard events
  • You can update the GameActivity package outside of Engine updates
  • You can tweak the new Unity<>Android Bridge with your own customisations

Better threading support
A major benefit to GameActivity is that many of the main application elements are now being handled by Android’s androidx.games:games-activity package, this includes:

  • Lifecycle events
  • Touchscreen keyboard
  • Input events

Google has published a use case where a reduction in ANRs was observed when using GameActivity. Since androidx.games:games-activity handles everything in the separate thread, we hope that you will also see similar benefits in your Unity applications.

Updatability
The AndroidX GameActivity Package is being maintained by Google and can be updated without updating the Unity version, it is simple enough to tweak the androidx.games:games-activity package version in unityLibrary/build.gradle file. So any bug fixes and improvements can be received without waiting for a Unity Engine update.

GameActivity-to-Unity bridge
As part of the Game Activity work we’ve introduced gameactivity-to-unity bridge layer.

If you switch to GameActivity you will also be able to tweak or change the bridge code (between Unity and androidx.games:games-activity package). This will give you even more flexibility over the events like pause/resume, which was previously impossible with Activity based applications. Read more about the bridge here.

Future of Game Activity

Our ambition is that GameActivity application entry point will become the main selection for the Android based applications created with Unity in the future. In 2023.1 the default application entry point remains Activity, and you have to turn GameActivity on explicitly. IIn Unity 2023.2, GameActivity will be turned on by default for new projects. When upgrading older projects to Unity 2023.2 the application entry setting will be preserved.

I’m sold - how can I try it out?

You can find the documentation for GameActivity here. The application entry setting can be found in Player Settings (Android):

Depending on what value you choose - the Gradle project will contain the following entry points:

  • UnityPlayerActivity.java - if Activity is chosen.
  • UnityPlayerGameActivity.java - if GameActivity is chosen.

As seen in the screenshot, you can both select Activity and GameActivity at the same time, this useful for testing purposes, since you don’t need to rebuild the application to run the application with Activity or GameActivity.

Note: If there are two activities selected, you’ll see two applications icons on your android device.

If you want to debug your application in AndroidStudio:

  • Export a Gradle project with Activity and GameActivity selected
  • Open the project in Android Studio
  • Under Run/Debug Configurations->General->Launch Activity->Launch Options pick Specified Activity
  • This way you can quickly switch between two application entry points and compare the behaviour of your application

Note: When building a non development application, Unity will warn you if there are more than one application entry point selected, since it’s not suitable for uploading to the store. Selecting two application entry points is only suitable for development purposes.

To switch your application to only use GameActivity application entry point, simply go to PlayerSettings (Android), locate Application Entry Point setting, deselect Activity and select GameActivity.

A note about plugins & dependencies
Plugin Creators

We expect that most plugins should work the same for both application entry points - Activity and GameActivity. Known exceptions:

  • Your plugin explicitly references UnityPlayerActivity.

If the app using your plugin targets only GameActivity, UnityPlayerActivity won’t exist. We advice not to use UnityPlayerActivity or UnityPlayerGameActivity classes directly in your plugin. It’s better to detect the current Activity with UnityPlayer.currentActivity .

If you target GameActivity, the main game looper runs on the native thread (created via pthread API) thus there’s no java Looper associated with that thread, calling that API will cause a null reference exception. You can use getMainLooper instead. Note: This looper runs on main/UI thread, if you have a long operation, it’s not recommended to use this looper, otherwise you’ll get ANR.

You can also create Java thread manually and create a looper on this thread instead, but you’ll need to handle synchronisation between game looper thread and your manually created Java thread

Dependencies

When targeting GameActivity, Unity adds the following dependencies into unityLibrary\build.gradle:

  • androidx.appcompat:appcompat
  • androidx.games:games-activity
  • androidx.core:core
  • androidx.constraintlayout:constraintlayout

If you use auto-resolution tools like Google’s EDM4U, you need to create the following templates to avoid conflicts during the gradle.build:

  • Main Gradle Template
  • Gradle Properties Template
  • Gradle Settings Template

We need your feedback!

We can’t wait to see how you use GameActivity and the bridge layer in your projects, and if you observe lifecycle/ANR improvements. Your feedback is very important to us - please let us know if you encounter any issues or if you have suggestions to share.

—-

Known Issues

  • You cannot enter characters with physical keyboard while touchscreen keyboard is up, should be fixed in androidx.games:games-activity 2.0.0 version

  • Sometimes application crashes in GameActivityMotionEvent_getHistoricalAxisValue when introducing a lot of motion events, should be fixed in androidx.games:games-activity 2.0.0 version

7 Likes

While rest of the feature set is great, EDMU part is disappointing! If we need to force the plugin users to have the custom gradle templates on, it takes a lot of hit on learning curve and also tough to manage.

Are there any alternatives for this or any specific reason there will be conflicts, if so what kind of conflicts?

We’re in talks with Google trying to resolve this, but basically when not using templates, EDM4U is downloading packages as .aar files and puts them in Assets\Plugins\Android, those end up in the generated gradle project and in some cases they conflict with dependencies added for GameActivity:

  • androidx.appcompat:appcompat
  • androidx.games:games-activity
  • androidx.core:core
  • androidx.constraintlayout:constraintlayout

As you might be aware how much of a big problem(conflict resolution among different plugins) EDM4U is solving, making it compatible would be a great move. Else, it breaks lots of plugins.

Coming to the solution, as you guys being creator and have more control, can this suggestion work?

  • Don’t include any dependency aars/libraries (like Play Core unity plugin/Unity IAP does)
  • Have a hook where you can modify the dependencies in the gradle file once its created (in worst case you may need to end up creating a gradle file parser but it works for all of your plugins which makes it great!)

This way you can actually avoid the conflicts and should be followed by all of your android plugins to make it compatible with existing plugins.

To be honest, we get many complaints about Unity IAP which includes billing client library leading to conflicts with our billing feature (while we use EDM4U for all of our plugins as its a god sent to resolve conflicts). Unfortunately, I see Unity IAP is almost added by default leading to more support requests, but what we can do, it’s Unity that is doing which keeps us quite :smile:

@Tomas1856 Very exciting! Out of curiosity, is it possible to use this as a custom activity for previous versions of Unity with some extra effort? Or is it heavily reliant on 2023? Our team would love to try this out but are stuck on LTS versions for the foreseeable future.

Sadly its impossible with earlier Unity versions, since we had to refactor some code in core engine, additonally we exposed some hooks. So without those things, its not possible to connect GameActivity to Unity runtime

Ah darn! Thank you for confirming.

@Tomas1856 Any thoughts on this?

Regarding Unity IAP, you would need to submit a feedback/bug for them, since there’s a dedicated team working on this package.

Starting Unity 2023.1, we’ll provide API for managing gradle files - https://docs.unity3d.com/2023.1/Documentation/Manual/android-modify-gradle-project-files-agp.html, it might already provide the functionality you want, if not, we would love to get your feedback on this.

Wow! This will definitely help to automate as required! Thanks for sharing @Tomas1856

2 Likes

anyone who used the string “UnityPlayerActivity” in their Android plug-in is gonna have a bad time

@Tomas1856 Any control exposed for UnityPlayer? For example for getting the surface view within it or changing with our own surface view?

Can you provide examples where you would use specifically UnityPlayerActivity in the plugins? Usually I think it’s enough to use UnityPlayer.currentActivity which would return Activity instance in both cases (UnityPlayerActivity and UnityPlayerGameActivity)

Check UnityPlayerGameActivity.onCreateSurfaceView, there’s a variable mSurfaceView, also UnityPlayer class has getView function, which also returns surface view instance.

As for overriding surface view with your own, check https://android.googlesource.com/platform/frameworks/opt/gamesdk/+/refs/heads/master/game-activity/src/main/java/com/google/androidgamesdk/GameActivity.java#251, I think you can override createSurfaceView and provide your own surface view, but it has to derive from InputEnabledSurfaceView. I haven’t tried this myself, so not sure how fluent the process is

We must config only one Entry activity to Meta(Facebook) app console, right?

Yes, I am actually not sure what would happen if you would submit an app with two activities enabled, would store deny such submission, since that would produce to application icons with the same name and icon, which obviously would be confusing.

Can you please verify that your integration is not affected by issue #276316114

Certainly don’t want gamepad input to be dependent on touch events…

I don’t think this is an issue in Unity, since Unity has code which handles key down/up events happening in the same frame. Though I see that this is incorrectly handled by input system package, but that would be a bug in input system package, so I think in this case Google is correct in saying, that they shouldn’t introduce any artificial delays, it’s up to game engine to handle this gracefully.

In any case, thank you for highlighting this.

I tried switching to GameActivity in a project of mine, but Unity Core Services logs an error as a result.

2023.07.10 17.32.32.903 3237 3271 Error Unity JNI:GetInternetReachability:java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,10,main] that has not called Looper.prepare()
2023.07.10 17.32.32.903 3237 3271 Error Unity Unity.Services.Core.Telemetry.Internal.DiagnosticsHandler:FetchSpecificCommonTags(ICloudProjectId, IEnvironments)
2023.07.10 17.32.32.903 3237 3271 Error Unity Unity.Services.Core.Telemetry.Internal.TelemetryHandler`2:Initialize(ICloudProjectId, IEnvironments)
2023.07.10 17.32.32.903 3237 3271 Error Unity Unity.Services.Core.Telemetry.Internal.TelemetryUtils:CreateDiagnosticsFactory(IActionScheduler, IProjectConfiguration, ICloudProjectId, IEnvironments)
2023.07.10 17.32.32.903 3237 3271 Error Unity Unity.Services.Core.Registration.CorePackageInitializer:InitializeDiagnostics(IActionScheduler, IProjectConfiguration, ICloudProjectId, IEnvironments)
2023.07.10 17.32.32.903 3237 3271 Error Unity Unity.Services.Core.Registration.<Initialize>d__42:MoveNext()
2023.07.10 17.32.32.903 3237 3271 Error Unity System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
2023.07.10 17.32.32.903 3237 3271 Error Unity System.Runtime.CompilerServices.MoveNextRunner:Run()
2023.07.10 17.32.32.903 3237 3271 Error Unity System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
2023.07.10 17.32.32.903 3237 3271 Error Unity System.Threading.Tasks.Task:FinishContinuations()

Core Services initialization attempt is simple await UnityServices.InitializeAsync(); in the main thread.

Am I doing something wrong?

Thanks for reporting, it looks like a bug we missed. We’ll try to fix it asap.

1 Like