I have a libGDX application, I have released numerous versions on the Google Play Store for many months now. However now I have encountered a problem I haven't had any luck in fixing.
The application works fine in Eclipse when I launch it on my android device, but when I export the APK and upload it to the Play Store and download the Alpha Test version, it crashes with this error:
08-27 13:01:39.000: E/AndroidRuntime(16968): FATAL EXCEPTION: GLThread 6159
08-27 13:01:39.000: E/AndroidRuntime(16968): Process: com.xaoilin.soloshape, PID: 16968
08-27 13:01:39.000: E/AndroidRuntime(16968): com.badlogic.gdx.utils.bf: Error reading file: data/scroller/uiskin.json
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.a.a.j.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.a.a.j.<init>(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.a.a.j(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.a.a.<init>(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.c.a.b(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.c.a.<init>(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.f.a.<init>(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.f.c.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at a.a.a.b(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at a.a.a.l(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at a.a.a.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at a.a.k.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.xaoilin.f.b.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.b(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.backends.android.m.onDrawFrame(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1531)
08-27 13:01:39.000: E/AndroidRuntime(16968): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1248)
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: com.badlogic.gdx.utils.bf: Error reading file: data/scroller/uiskin.json
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.utils.o.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 17 more
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: com.badlogic.gdx.utils.bf:
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.a.a.l.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.a.a.l.b(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.utils.o.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.f.a.a.k.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 18 more
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: com.badlogic.gdx.utils.b.f: Class not found: com.badlogic.gdx.graphics.g2d.BitmapFont
08-27 13:01:39.000: E/AndroidRuntime(16968): at com.badlogic.gdx.utils.b.b.a(Unknown Source)
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 22 more
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: java.lang.ClassNotFoundException: com.badlogic.gdx.graphics.g2d.BitmapFont
08-27 13:01:39.000: E/AndroidRuntime(16968): at java.lang.Class.classForName(Native Method)
08-27 13:01:39.000: E/AndroidRuntime(16968): at java.lang.Class.forName(Class.java:251)
08-27 13:01:39.000: E/AndroidRuntime(16968): at java.lang.Class.forName(Class.java:216)
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 23 more
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: java.lang.NoClassDefFoundError: com/badlogic/gdx/graphics/g2d/BitmapFont
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 26 more
08-27 13:01:39.000: E/AndroidRuntime(16968): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.badlogic.gdx.graphics.g2d.BitmapFont" on path: DexPathList[[zip file "/data/app/com.xaoilin.soloshape-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.xaoilin.soloshape-1, /vendor/lib, /system/lib]]
08-27 13:01:39.000: E/AndroidRuntime(16968): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:67)
08-27 13:01:39.000: E/AndroidRuntime(16968): at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
08-27 13:01:39.000: E/AndroidRuntime(16968): at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
08-27 13:01:39.000: E/AndroidRuntime(16968): ... 26 more
As you can see, the first error I get is reading a file: data/scroller/uiskin.json
This is the line that attempts to read this file in my code:
// Loads the ui's skin to be used on this example:
uiSkin = new Skin(Gdx.files.internal("data/scroller/uiskin.json"),
assetManager.get("data/scroller/uiskin.atlas", TextureAtlas.class));
It's important to note this file NEVER caused a live release to crash, in fact, it's completely irrelevant to the new feature I was implementing in the app. I was implementing GdxPay as in the Gdx-Wiki and tested the results offline and everything works as expected.
However as mentioned, when uploading to the Play Store for some reason it crashes when loading a random json file in a totally separate area of my code base.
This is what happens when launching the app on my device through Eclipse: the app loads the splash screen using Tween Engine, and then automatically loads the game screen successfully.
This is what happens when launching the app through Google Play Store released as Alpha Testing: the app loads the splash screen successfully, and then crashes with the error message above about not being able to read the json file.
My only guess as to the cause of this is the exported APK is different to the APK I'm installing when using Eclipse. I've tested this on both a samsung Tablet and Smart Phone and both yield the same results as above.
I understand proguard config affects the exported APK and I've changed the config settings a few times but it still crashes all the same when downloading and running from the Play Store.
package com.xaoilin.soloshape;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.pay.Offer;
import com.badlogic.gdx.pay.OfferType;
import com.badlogic.gdx.pay.PurchaseManagerConfig;
import com.badlogic.gdx.pay.PurchaseObserver;
import com.badlogic.gdx.pay.Transaction;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.xaoilin.SSHelpers.AssetLoader;
import com.xaoilin.Screens.SplashScreen;
public class SSGame extends Game {
// ----- app stores -------------------------
public static final int APPSTORE_UNDEFINED = 0;
public static final int APPSTORE_GOOGLE = 1;
public static final int APPSTORE_OUYA = 2;
public static final int APPSTORE_AMAZON = 3;
public static final int APPSTORE_DESKTOP = 4;
private int isAppStore = APPSTORE_UNDEFINED;
public final static String productID_fullVersion = "fullversion";
public static PlatformResolver m_platformResolver;
public PurchaseManagerConfig purchaseManagerConfig;
public PurchaseObserver purchaseObserver = new PurchaseObserver() {
@Override
public void handleRestore (Transaction[] transactions) {
for (int i = 0; i < transactions.length; i++) {
if (checkTransaction(transactions[i].getIdentifier(), true) == true) break;
}
}
@Override
public void handleRestoreError (Throwable e) {
throw new GdxRuntimeException(e);
}
@Override
public void handleInstall () { }
@Override
public void handleInstallError (Throwable e) {
Gdx.app.log("ERROR", "PurchaseObserver: handleInstallError!: " + e.getMessage());
throw new GdxRuntimeException(e);
}
@Override
public void handlePurchase (Transaction transaction) {
checkTransaction(transaction.getIdentifier(), false);
}
@Override
public void handlePurchaseError (Throwable e) { //--- Amazon IAP: this will be called for cancelled
throw new GdxRuntimeException(e);
}
@Override
public void handlePurchaseCanceled () { //--- will not be called by amazonIAP
}
};
protected boolean checkTransaction (String ID, boolean isRestore) {
boolean returnbool = false;
if (productID_fullVersion.equals(ID)) {
Gdx.app.log("checkTransaction", "full version found!");
//----- put your logic for full version here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
returnbool = true;
}
return returnbool;
}
public SSGame () {
setAppStore(APPSTORE_GOOGLE); // change this if you deploy to another platform
// ---- IAP: define products ---------------------
purchaseManagerConfig = new PurchaseManagerConfig();
purchaseManagerConfig.addOffer(new Offer().setType(OfferType.CONSUMABLE).setIdentifier(productID_fullVersion));
}
@Override
public void create() {
getPlatformResolver().requestPurchaseRestore(); // check for purchases in the past
AssetLoader.load();
setScreen(new SplashScreen(this));
// setScreen(new GameScreen());
}
@Override
public void dispose() {
super.dispose();
AssetLoader.dispose();
}
public PlatformResolver getPlatformResolver() {
return m_platformResolver;
}
public static void setPlatformResolver (PlatformResolver platformResolver) {
m_platformResolver = platformResolver;
}
public int getAppStore () {
return isAppStore;
}
public void setAppStore (int isAppStore) {
this.isAppStore = isAppStore;
}
}
package com.xaoilin.soloshape;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import android.content.Intent;
import android.os.Bundle;
public class AndroidLauncher extends AndroidApplication {
SSGame game;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
game = new SSGame();
initialize(game, config);
if (game.getAppStore() == SSGame.APPSTORE_GOOGLE) {
SSGame.setPlatformResolver(new GooglePlayResolver(game));
}
game.getPlatformResolver().installIAP();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onDestroy() {
super.onDestroy();
// InApp: dispose payment system(s)
game.getPlatformResolver().dispose();
}
}
If anyone knows what's causing the app to crash on live but works fine locally it would be a huge help. Thanks in advance!
Tldr; Android LibGDX app works fine locally when launching on android device, but when uploading to and launching from the Play Store it crashes by failing to load a random file in a random area of my code which has never given me a problem on any of my previous releases.
-verbose
-dontwarn javax.annotation.Nullable
-dontwarn android.support.**
-dontwarn com.badlogic.gdx.backends.android.AndroidFragmentApplication
-dontwarn com.badlogic.gdx.utils.GdxBuild
-dontwarn com.badlogic.gdx.physics.box2d.utils.Box2DBuild
-dontwarn com.badlogic.gdx.jnigen.BuildTarget*
-dontwarn com.badlogic.gdx.graphics.g2d.freetype.FreetypeBuild
-keep class com.badlogic.gdx.controllers.android.AndroidControllers
-keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* {
<init>(com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration);
}
-keepclassmembers class com.badlogic.gdx.physics.box2d.World {
boolean contactFilter(long, long);
void beginContact(long);
void endContact(long);
void preSolve(long, long);
void postSolve(long, long);
boolean reportFixture(long);
float reportRayFixture(long, float, float, float, float, float);
}
-keep class com.android.vending.billing.**
-keep class com.amazon.** {*;}
-keep class com.sec.android.iap.**
-keep class com.nokia.payment.iap.aidl.**
-keep class org.onepf.oms.appstore.**
-dontwarn org.onepf.oms.appstore.**
-keep public class com.badlogic.gdx.Gdx {
public protected *;
}
-keep public class com.badlogic.gdx.Application {
public protected *;
}
-keep public class com.badlogic.gdx.pay.android.IAP {
public protected *;
}
-keep public class com.badlogic.gdx.backends.android.AndroidEventListener {
public protected *;
}
-keep public class com.badlogic.gdx.backends.android.AndroidApplication {
public protected *;
}
-keep public class com.badlogic.gdx.pay.android.openiab.PurchaseManagerAndroidOpenIAB {
public protected *;
}
-keep public class com.badlogic.gdx.pay.android.googleplay.AndroidGooglePlayPurchaseManager {
public protected *;
}
As @Polarbear0106 said, components of libGDX were not being included in the APK. The fix that worked for me was to add this line in the proguard-project.txt file:
"-keep class com.badlogic.** {*;}"
It then exported correctly and uploaded to the Play Store and the live version no longer crashes!