question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Android] Loading of natives in static intializer makes it impossible to gracefully handle missing native libs

See original GitHub issue

Issue details

Because GdxNativesLoader.load() is called in the static initializer of AndroidApplication, it’s not possible to run any code before it. This means that if native libs are not present the app immediately crashes, with no way to check for missing natives and handle the situation more gracefully (such as by displaying an error popup and closing the app).

This is an issue because of Google’s new app bundle functionality on Google Play. When app bundles are used, Google Play only distributes necessary resources to each device, including natives. This results in some nice space savings, but it also makes it very easy for someone to sideload the wrong APK and not get the natives they need.

I would think a better approach would be to load native libs in AndroidApplication.onCreate. This way code to check native libs could be run before calling super.onCreate. LibGDX could even perform the checking itself and display a standard error message, which a developer could then customize/override by manually checking earlier. This is much preferable to crashing as it won’t affect an application’s stability statistics on Google Play, and gives an opportunity to direct users to an APK which would work for them.

I’m not very familiar with the LibGDX codebase, so it’s possible I’m missing something here, but if there is agreement that moving native initialization to AndroidApplication.onCreate is a good idea I’d be happy to submit a pull request.

Reproduction steps/code

Can be reproduced in any libgdx android project by simply removing native libraries and commenting out native dependencies in build.gradle

Version of LibGDX and/or relevant dependencies

Tested on 1.9.10 but should affect all versions.

Stacktrace

(Example stack trace from a user crash on my game: Shattered Pixel Dungeon)

java.lang.ExceptionInInitializerError: 
  at java.lang.Class.newInstance (Class.java)
  at android.app.AppComponentFactory.instantiateActivity (AppComponentFactory.java:69)
  at androidx.core.app.CoreComponentFactory.instantiateActivity (CoreComponentFactory.java:43)
  at android.app.Instrumentation.newActivity (Instrumentation.java:1215)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3008)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3257)
  at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:78)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1948)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7050)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by: com.badlogic.gdx.utils.GdxRuntimeException: 
  at com.badlogic.gdx.utils.SharedLibraryLoader.load (SharedLibraryLoader.java:125)
  at com.badlogic.gdx.utils.GdxNativesLoader.load (GdxNativesLoader.java:33)
  at com.badlogic.gdx.backends.android.AndroidApplication.<clinit> (AndroidApplication.java:60)
  at java.lang.Class.newInstance (Class.java)
  at android.app.AppComponentFactory.instantiateActivity (AppComponentFactory.java:69)
  at androidx.core.app.CoreComponentFactory.instantiateActivity (CoreComponentFactory.java:43)
  at android.app.Instrumentation.newActivity (Instrumentation.java:1215)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3008)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3257)
  at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:78)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1948)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7050)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by: java.lang.UnsatisfiedLinkError: 
  at java.lang.Runtime.loadLibrary0 (Runtime.java:1012)
  at java.lang.System.loadLibrary (System.java:1669)
  at com.badlogic.gdx.utils.SharedLibraryLoader.load (SharedLibraryLoader.java:119)
  at com.badlogic.gdx.utils.GdxNativesLoader.load (GdxNativesLoader.java:33)
  at com.badlogic.gdx.backends.android.AndroidApplication.<clinit> (AndroidApplication.java:60)
  at java.lang.Class.newInstance (Class.java)
  at android.app.AppComponentFactory.instantiateActivity (AppComponentFactory.java:69)
  at androidx.core.app.CoreComponentFactory.instantiateActivity (CoreComponentFactory.java:43)
  at android.app.Instrumentation.newActivity (Instrumentation.java:1215)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3008)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3257)
  at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:78)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1948)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7050)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)

Please select the affected platforms

  • Android
  • iOS (robovm)
  • iOS (MOE)
  • HTML/GWT
  • Windows
  • Linux
  • MacOS

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:18 (18 by maintainers)

github_iconTop GitHub Comments

4reactions
MrStahlfelgecommented, Jul 7, 2021
  • We have a problem here
  • We have code that we know is not good here
  • We have no backwards compatiblity break for any games that doesn’t tweak special parts of the Android implementation, so I would state there is no breaking change

Hence I think we should make a change and not switch to “never change a running system” mode just because changes might introduce new problems. If we know the static initializers are a problem, that should be changed. If the change introduces new problems, then it should be changed again.

1reaction
00-Evancommented, Jul 4, 2021

You can use Android UI classes (e.g. textview or alertdialog) to display errors in the libGDX activity just the same as a separate one. It’s probably a bad idea to expect the libGDX classes to gracefully handle cases where initialization fails either way though. Will just move loading to init with no other changes and submit a pull request soon.

Read more comments on GitHub >

github_iconTop Results From Across the Web

why loading JNI in java is done in static initializer?
Before the native method print can be called, the native library that implements print must be loaded. In this case, we load the...
Read more >
jogamp.org/deployment/v2.2.0/archive/ChangeLogs/jo...
Bug 1021: OVR GlueGen Mapping: Handle non-existent native oculusvr lib gracefully Fix build if oculusvr-sdk submodule is missing GLU: Make ...
Read more >
Bug listing with status RESOLVED with resolution OBSOLETE ...
Bug:1523 - "[IDEA] Offload work by distributing trivial ebuild maintenance to users, introduce a simple stability voting system and have a core team...
Read more >
Release Notes | Firebase - Google
December 08, 2022. SDK Releases. The Firebase Android SDKs for Cloud Messaging have been updated. For more ...
Read more >
How To Improve Appium Test Speed and Reliability - HeadSpin
This is just one example of many possible examples in the category of test code assuming the app is in a certain state,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found