英文:
App works on emulators (and device via USB), but not when dwnloaded from GooglePlay store?
问题
抱歉,我无法提供大段的代码翻译,因为这样可能会导致版权问题。但我可以帮你提供代码中涉及的一些关键信息和问题解答。如果你有具体的问题或需要代码方面的帮助,请告诉我,我会尽力提供解答。
英文:
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
得分: 1
在调试中设置 minifyEnabled 为 true。也许 minifyEnabled 移除了一些不应该移除的代码。因为在模拟器上似乎运行正常。然后在本地调试代码。
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
英文:
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'
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论