App works on emulators (and device via USB), but not when dwnloaded from GooglePlay store?

Viewed 76

Apologies, I know there are similar questions on the forum already, but none of them seem to be quite the same issue as I'm running into here. I've just completed an Android tutorial course, and built and released my very first app - I'm a newbie so forgive me if I've done something stupid!

My app works fine on the laptop - on a variety of emulators, with a variety of SDK versions, and also works on a real device when plugged into the laptop with a USB cable. However, when I upload it onto the GooglePlay store (and wait 3 days for it to get reviewed!) it then doesn't work when I download it - onto the same device as was connected via USB.

The app crashes before the first page even opens, and the only error info I can get it the error info provided by GooglePlay - included below. The problem appears to be with my Parse Initialisation, but I don't know what or why? Any help would be much appreciated!

GOOGLEPLAY ERROR LOG:

java.lang.RuntimeException: 
  at android.app.ActivityThread.handleMakeApplication (ActivityThread.java:7041)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6989)
  at android.app.ActivityThread.access$1600 (ActivityThread.java:272)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2055)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:237)
  at android.app.ActivityThread.main (ActivityThread.java:8016)
  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:1076)
Caused by: java.lang.IllegalArgumentException: 
  at com.parse.ParseObjectSubclassingController.registerSubclass (ParseObjectSubclassingController.java:75)
  at com.parse.ParseObject.registerSubclass (ParseObject.java:491)
  at com.parse.ParseObject.registerParseSubclasses (ParseObject.java:3486)
  at com.parse.Parse.initialize (Parse.java:395)
  at com.baileycoding.StarterApplication.onCreate (StarterApplication.java:29)
  at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1190)
  at android.app.ActivityThread.handleMakeApplication (ActivityThread.java:7036)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6989)
  at android.app.ActivityThread.access$1600 (ActivityThread.java:272)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2055)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:237)
  at android.app.ActivityThread.main (ActivityThread.java:8016)
  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:1076)

APP MANIFEST.XML:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.baileycoding"
    android:targetSandboxVersion="1" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:name="com.baileycoding.StarterApplication"
        android:allowBackup="true"
        android:usesCleartextTraffic="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.Light">
        <activity android:name="com.baileycoding.AboutActivity"></activity>
        <activity android:name="com.baileycoding.WineResult" />
        <activity android:name="com.baileycoding.MyResultsActivity" />
        <activity android:name="com.baileycoding.ScoreboardActivity" />
        <activity android:name="com.baileycoding.RatingActivity" />
        <activity android:name="com.baileycoding.GuessDescriptionActivity" />
        <activity android:name="com.baileycoding.GuessTastesActivity" />
        <activity android:name="com.baileycoding.GuessGrapeActivity" />
        <activity android:name="com.baileycoding.GuessCRVActivity" />
        <activity android:name="com.baileycoding.WineListActivity" />
        <activity android:name="com.baileycoding.StartGameActivity" />
        <activity android:name="com.baileycoding.WineAssignmentActivity" />
        <activity android:name="com.baileycoding.PrepActivity" />
        <activity android:name="com.baileycoding.WineInfoActivity2" />
        <activity
            android:name="com.baileycoding.WineInfoActivity"
            android:windowSoftInputMode="adjustPan" />
        <activity android:name="com.baileycoding.EventIdConfirmationActivity" />
        <activity
            android:name="com.baileycoding.CreateNewEventActivity"
            android:windowSoftInputMode="adjustPan" />
        <activity android:name="com.baileycoding.EventMenuActivity" />
        <activity android:name="com.baileycoding.InitialMenuActivity" />
        <activity
            android:name="com.baileycoding.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />
    </application>

</manifest>

BULD.GRADLE (module):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion '22.0.1'

    defaultConfig {
        applicationId "com.baileycoding.thegrapeunknown"
        minSdkVersion 26
        targetSdkVersion 29
        versionCode 2
        versionName '1.1'
        multiDexEnabled true
    }
    dexOptions {
        javaMaxHeapSize "4g"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    compile 'com.android.support:appcompat-v7:26.0.0'
    compile 'com.parse.bolts:bolts-tasks:1.3.0'
    compile 'com.parse:parse-android:1.13.0'
    compile 'com.google.android.gms:play-services:9.4.0'
    compile 'com.android.support:multidex:1.0.0'
}

BUILD.GRADLE (project):

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        mavenCentral()
        jcenter()
        google()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.0'
    }
}

allprojects {
    repositories {
        mavenCentral()
        google()
    }
}

ext {
    compileSdkVersion = 22
    buildToolsVersion = "23.0.1"

    minSdkVersion = 9
    targetSdkVersion = 23
    defaultVersionCode = 1.1
}

PARSE STARTER ACTIVITY:

package com.baileycoding;

import android.app.Application;

import com.parse.Parse;
import com.parse.ParseACL;


public class StarterApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();

    // Enable Local Datastore.
    Parse.enableLocalDatastore(this);

    // Add your initialization code here
    Parse.initialize(new Parse.Configuration.Builder(getApplicationContext())
            .applicationId("myappID")
            .clientKey("xxxxxxxxxxx") //hidden for security - let me know if useful to see
            .server("http://18.220.164.203/parse/")
            .build()
    );


    //PARSE SETUP STATEMENTS
    //ParseUser.enableAutomaticUser();

    ParseACL defaultACL = new ParseACL();
    defaultACL.setPublicReadAccess(true);
    defaultACL.setPublicWriteAccess(true);
    ParseACL.setDefaultACL(defaultACL, true);


  }
}

MAIN ACTIVITY. JAVA:

package com.baileycoding;

import android.content.Intent;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.parse.FindCallback;
import com.parse.LogInCallback;
import com.parse.ParseAnalytics;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import com.parse.SignUpCallback;
import com.baileycoding.R;

import java.util.List;


public class MainActivity extends AppCompatActivity implements View.OnKeyListener, View.OnClickListener {

    EditText emailEditText;
    EditText passwordEditText;
    EditText nameEditText;
    Button loginButton;
    TextView switchTextView;
    ConstraintLayout backgroundLayout;

    Boolean loginMode; // 1 = signup, 0 = login
    String email;
    String password;
    String name;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTheme(R.style.MyTheme);
        setContentView(R.layout.activity_main);

        //setTitle("Welcome");
        if (getSupportActionBar() != null) {
            getSupportActionBar().hide();
        }

        emailEditText = (EditText) findViewById(R.id.emailEditText);
        passwordEditText = (EditText) findViewById(R.id.passwordEditText);
        nameEditText = (EditText) findViewById(R.id.nameEditText);
        loginButton = (Button) findViewById(R.id.loginButton);
        switchTextView = (TextView) findViewById(R.id.switchTextView);
        backgroundLayout = findViewById(R.id.backgroundLayout);

        passwordEditText.setOnKeyListener(this);
        nameEditText.setOnKeyListener(this);
        backgroundLayout.setOnClickListener(this);

        loginMode = false;
        nameEditText.setVisibility(View.GONE);

        ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
        query.whereNotEqualTo("username","test");
        query.findInBackground(new FindCallback<ParseObject>() {
            @Override
            public void done(List<ParseObject> objects, ParseException e) {
                if(e!=null || objects.size()==0){
                    Toast.makeText(MainActivity.this, "You are not currently logged in", Toast.LENGTH_SHORT).show();
                    if(e!=null){ e.printStackTrace();}
                } else {
                    if (ParseUser.getCurrentUser() != null) {
                        String currentUser = ParseUser.getCurrentUser().toString();
                        //ParseUser.logOut();
                        Toast.makeText(MainActivity.this, "Currently logged in as " + ParseUser.getCurrentUser().getUsername(), Toast.LENGTH_SHORT).show();
                        moveToInitialMenu();
                    }
                }
            }
        });


        ParseAnalytics.trackAppOpenedInBackground(getIntent());
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
            loginOrSignup(v);
        }
        return false;
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.backgroundLayout) {
            InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getWindow().getDecorView().getRootView().getWindowToken(), 0);
        }
    }

    public void switchToSignup(View view) {

        if (loginMode == false) {
            loginMode = true;
            Log.i("Login mode switched to", "true; signup mode");
            loginButton.setText("SIGNUP");
            switchTextView.setText("Already have an account? Log in here");
            nameEditText.setVisibility(View.VISIBLE);
        } else {
            loginMode = false;
            Log.i("Login mode switched to", "false; login mode");
            loginButton.setText("LOGIN");
            switchTextView.setText("Don't have an account? Sign up here");
            nameEditText.setVisibility(View.GONE);
        }
    }

    public void loginOrSignup(View view) {
        loginButton.setEnabled(false);
        email = emailEditText.getText().toString();
        password = passwordEditText.getText().toString();
        name = nameEditText.getText().toString();

        if (loginMode) {
            if (email.matches("") || password.matches("") || name.matches("")) {
                Toast.makeText(this, "Please enter email, password and a display name", Toast.LENGTH_SHORT).show();
            } else {
                //SIGNUP USER
                ParseUser user = new ParseUser();
                user.setUsername(email);
                user.setPassword(password);
                user.put("displayName", name);
                user.signUpInBackground(new SignUpCallback() {
                    @Override
                    public void done(ParseException e) {
                        if (e == null) {
                            Toast.makeText(MainActivity.this, "Successfully created new user", Toast.LENGTH_SHORT).show();
                            moveToInitialMenu();
                        } else {
                            e.printStackTrace();
                            Toast.makeText(MainActivity.this, "Sign Up failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
                            loginButton.setEnabled(true);
                        }
                    }
                });
            }
        } else {
            if (email.matches("") || password.matches("")) {
                Toast.makeText(this, "Please enter email and password", Toast.LENGTH_SHORT).show();
            } else {
                //LOGIN USER
                ParseUser.logInInBackground(email, password, new LogInCallback() {
                    @Override
                    public void done(ParseUser user, ParseException e) {
                        if (e == null) {
                            Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
                            moveToInitialMenu();
                        } else {
                          loginButton.setEnabled(true);
                          e.printStackTrace();
                            Toast.makeText(MainActivity.this, "Login failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
            }
        }


    }

    public void moveToInitialMenu() {
        Intent intent = new Intent(getApplicationContext(), InitialMenuActivity.class);
        startActivity(intent);
    }

}

MAIN ACTIVITY.XML:


<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/backgroundLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@drawable/backgroundv1"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/loginButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:fontFamily="@font/delius"
        android:onClick="loginOrSignup"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="LOG IN"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.495"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/nameEditText" />

    <EditText
        android:id="@+id/emailEditText"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="290dp"
        android:ems="10"
        android:fontFamily="@font/delius"
        android:hint="Email address"
        android:inputType="textEmailAddress"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/passwordEditText"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:fontFamily="@font/delius"
        android:hint="Password"
        android:inputType="textPassword"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/emailEditText" />

    <EditText
        android:id="@+id/nameEditText"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:ems="10"
        android:fontFamily="@font/delius"
        android:hint="Name (to display to others)"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/passwordEditText" />


    <TextView
        android:id="@+id/switchTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:fontFamily="@font/delius"
        android:onClick="switchToSignup"
        android:text="Don't have an account yet? Sign up here instead"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.495"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/loginButton" />
</android.support.constraint.ConstraintLayout>

1 Answers

Set minifyEnabled true in debug. Maybe minifyEnabled removed some code that it shouldn't. Since it seems to work fine on emulator. And then debug the code locally.

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    debug {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
Related