Search code examples
androidlibgdxcrashgoogle-playin-app-purchase

Android LibGDX application works locally but crashes on alpha testing


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.

Launching

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.

For Reference

My Core Game Class

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;
    }
}

My Android Launcher Activity

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.

EDIT

Proguard File

-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 *;
}

Solution

  • 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!