All works well. Once the app is running, and I press home and back to the app through the multitask viewer, it works well. But once it is running, and I press it's icon from the drawer, it crashes because it is calling again "setPersistenceEnabled()" when it is already running. So, how can I check if it is enabled before trying to enable it? My code:
public class SplashActivity extends AppCompatActivity {
private FirebaseUser firAuth;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getInstance().setPersistenceEnabled(true);
firAuth = FirebaseAuth.getInstance().getCurrentUser();
if (firAuth!=null) {
// User is signed in.
Intent intent = new Intent(this, Identificador.class);
startActivity(intent);
finish();
} else {
// No user is signed in.
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
}
};
}
I recommend you using the following lines of code:
private static boolean calledAlready = false;
if (!calledAlready) {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
calledAlready = true;
}
Run your app in debug mode. Set a breakpoint at your SplashActivity onCreate() an run through your use case that crashes the app. You'll see that SplashActivity onCreate() is invoked every time you launch your app after you leave it, because that's how activities work when they're exited and re-entered.
If you want to prevent repeated calls to setPersistenceEnabled(), you'll have to guard them against the invocation of your launch activity's onCreate() by setting some global variable to indicate that it's already been called, or put the call in a ContentProvider that gets created only once per process. I suggest the latter.
Related
I have an app with a MainActivity and a SecondaryActivity.
When the MainActivity opens the SecondaryActivity, there is a situation in which I need the user to pick up a phone number from a list that is in the MainActivity.
I've used startActivityForResult to open back the MainActivity with an extra in the intent so that the MainActivity opens directly the fragment with the specific list, like this:
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("UserInfo", userInfo);
intent.putExtra("SecondaryActivity", true);
startActivityForResult(intent, RESULT_FROM_MAIN_ACTIVITY);
And when the user clicks on one element of that list, I set the result and finish the MainActivity in order to return to the SecondaryActivity, like this:
Intent returnIntent = new Intent();
returnIntent.putExtra(PHONE_NUMBER, phoneNumber);
setResult(Activity.RESULT_OK, returnIntent);
finish();
That worked wonderfully until now, but I've noticed that in Android 11 devices, I have 2 problems:
startActivityForResult starts the MainActivity directly in the onResume() function without passing by the onCreate() function, as it is for Android 10 devices and below. And this forces me to check each time the MainActivity's onResume() function is called, if there is any extra sent. Thing that unnecessarily add an extra charge to the already charged MainActivity.
I cannot come back to the SecondaryActivity using finish()! Instead of that, the app is simply closed.
I've tried to use the new Activity Result APIs, and more specifically: registerForActivityResult, like this (in the SecondaryActivity):
private ActivityResultLauncher<Intent> activityResultLauncher;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
manageResult(result.getResultCode(), result.getData());
});
}
public void onPhoneNumberClick() {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("UserInfo", userInfo);
intent.putExtra("SecondaryActivity", true);
activityResultLauncher.launch(intent);
}
But, this doesn't change anything to the 2 problems that I described: onResume() is directly called in MainActivity and finish() close the app instead of closing the MainActivity and coming back to the SecondaryActivity. I understand, that the fact that the MainActivity was already opened when I use startActivityForResult or registerForActivityResult and launch can be the cause of those problems, but what can I do to solve them?
Edit:
My MainActivity is defined as singleTask in AndroidManifest and it seems that something changed in Android 11 for the management of activities defined like this. If I remove this tag, it solves the problem but I'm not sure why we defined this activity as singleTask and I don't want to see come back any bug that this was intended to solve...
MainActivity has an attribute android:launchMode="singleInstance" in AndroidManifest.xml
Here's the onCreate method from MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate() from MainActivity");
session = new TempSession(this);
// check if logged in. If not, take the user back to login activity.
session.checkLogin(); // <---- HERE
Toast.makeText(this, "logged in as " + session.getUsername(), Toast.LENGTH_SHORT).show();
// Prevents screen from turning off when in this Activity.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
....
and the TempSession, it checks if user is logged in, and if not, it will take the user to the LoginActivity.
This is the checkLogin() method
public void checkLogin(){
// Check login status
if(!this.isLoggedIn() || getUsername() == null) {
// user is not logged in redirect him to Login Activity
Intent i = new Intent(context, LoginActivity.class);
// Closing all the Activities
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Add new Flag to start new Activity
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
// Staring Login Activity
context.startActivity(i);
}
}
From my understanding, where I marked <---- HERE from MainActivity, when it calls checkLogin(), shouldn't it finish all task before it proceeds?
Currently, my app would open up LoginActivity, but still proceeds to the next instructions (e.g. makes a Toast saying "logged in as null", and keeps going on in background) and calls onPause() -> onStop() .
Is there any way to stop the MainActivity from keep going at that point?
This situation causes pressing back from LoginActivity bypasses to MainActivity.
Let me know if you need more information.
Thank you in advance.
Is there any way to stop the MainActivity from keep going at that point?
Yes. Have checkLogin() return some value that indicates MainActivity#onCreate(...) should return instead of proceeding.
Calling startActivity(...) does not start an activity. Think of it as asking the Android framework to start an activity. It places this job at the end of the UI thread's work queue. That means that before it starts the new activity, it will finish any other jobs that are already in the work queue, including the rest of the current method and possibly other lifecycle methods of the current activity.
The reason that your toast still shows up is because merely starting a new activity does not destroy your old activity. It's merely pushed to the backstack. Of course you can change this using the android:launchMode attribute in your manifest. A simple strategy in your cause might be to simply return true from your checkLogin() method when the user is logged in and change your onCreate as follows:
if( session.checkLogin() ) {
Toast.makeText(this, "logged in as " + session.getUsername(), Toast.LENGTH_SHORT).show();
// Prevents screen from turning off when in this Activity.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
....
}
When my project starts up it's fine. MainActivity starts and then it navigates to another page but when I navigate back to MainActivity I sometimes get this error. http://imgur.com/FVhWMHn
I'm positive this is from calling Parse.enableLocalDatastore before Parse.initialize but here is my code for mainActivity
Parse.enableLocalDatastore(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Parse.initialize(this, "MY_INFO", "MY_INFO_AGAIN");
// check if a user is not cached
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser == null)
{
// prompt user to LoginOrSignUp screen
Intent intent = new Intent(MainActivity.this, LoginOrSignUpActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}`
local data store is called before initialize but it still crashes. I don't get it. I can communicate with my data base just fine and everything but as soon as I navigate back to main it crashes
you should initialize your Parse SDK in separate class as follows
public class SampleApplication extends Application {
public void onCreate(){
super.onCreate();
Parse.enableLocalDatastore(getApplicationContext());
Parse.initialize(this, "PARSE_APP_KEY", "PARSE_CLIENT_KEY");
ParseInstallation.getCurrentInstallation().saveInBackground();
}
}
IMPORTANT!!! you need to put this class info in your AndroidManifest.xml file.
hope it helps!
im new in developing Android Apps.
I know how to set the starting Activity with the AndroidManifest.xml, but i need a way to check something first before i choose the starting Activity.
My Problem:
I created a loginActivity and a mainActivity.
I want to do the following: If i log me in, i'll set a flag to 1 and at the next app start, i want show directly the mainActivity and not the login.
Is there any way to do that? I thought about creating a splashscreen where i can check if im logged in before showing the first Activity.
Thanks, Philip
Updated Code - working:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get preferences
SharedPreferences userDetails = getApplicationContext().getSharedPreferences("userdetails", 0);
String savedEmail = userDetails.getString("email", "");
String savedPassword = userDetails.getString("password", "");
Boolean savedRemember = userDetails.getBoolean("remember", false);
Boolean savedLogged = userDetails.getBoolean("logged", false);
// check if already logged in
if(savedLogged) {
// start the overview
Intent intent = new Intent(this, ActivityOverview.class);
startActivity(intent);
finish();
}
else {
// initialize the form layout
setContentView(R.layout.activity_login);
// get views
this.email = (EditText)findViewById(R.id.editTextEmail);
this.password = (EditText)findViewById(R.id.editTextPassword);
this.remember = (CheckBox)findViewById(R.id.checkBoxRemember);
// set values to views
this.email.setText(savedEmail);
this.password.setText(savedPassword);
this.remember.setChecked(savedRemember);
}
}
I have something similar in one of my apps. I let the user chose weather it should auto-login or not. This is saved in the SharedPreferences.
When the app starts and in the mainpage, you should check - BEFORE the setContentView(R.layout.activity_login); and set finish(); after startActivity();
if(savedLogged) {
Intent intent = new Intent(this, ActivityOverview.class);
startActivity(intent);
finish();
}
setContentView(R.layout.activity_login);
You don't really need a splash screen.
When the user successfully logs into your app, set a boolean flag in SharedPreferences to true. Check if this flag is true in the login activity's onCreate() and if it is, use an Intent to launch the main activity.
You do not need to display your activity. Not sure what exactly your code flow is going to be but you can safely start i.e. with LoginActivity. in your onCreate() check if you are already logged (whatever it means for you) and if so instantly start MainActivity killing LoginActivity with finish().
Going with a splashscreen would be a good idea. Starting the loginActivity would show the login screen for a split second. This would be undesirable.
I'm pretty new to Android/java, and have come up against my first head-scratcher.
I'm trying to conditionally change the starting view based on if a user is logged in (I've set is_logged_in already, here, to more simply illustrate the problem).
Many thanks.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean is_logged_in = false;
if(is_logged_in) {
setContentView(R.layout.activity_main);
} else {
menu_sign_up(); // How would I pass the View?
}
}
// Doesn't work, at least when called inside of MainActivity's onCreate
public void menu_sign_up(/* View view */) {
Intent intent = new Intent(this, SignUp.class);
startActivity(intent);
}
// Example of a working method, at least when called from android:onClick in XML
public void menu_sign_in(View view) {
Intent intent = new Intent(this, SignIn.class);
startActivity(intent);
overridePendingTransition(0,0);
}
}
public class SignUp extends MainActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sign_up);
}
// ...
}
***** Truncated Stack Trace Below ******* (adb logcat *:W)
W/ActivityManager(19583): Activity idle timeout for HistoryRecord{40953fd8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(): Activity idle timeout for HistoryRecord{}
W/ActivityManager(21900): Launch timeout has expired, giving up wake lock!
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a23580 com.example.digitalkarmaeclipse/.MainActivity}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a2c6f8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a30100 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a338e8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a376e0 com.example.digitalkarmaeclipse/.SignUp}
Your current app structure doesn't make a lot of sense. Does SignUp have to be an Activity as well? You could use an AlertDialog to do something similar. If it does need to be an Activity then have it extend Activity, it doesn't make much sense to extend HomeActivity in this context.
I also don't like the idea of putting setContentView() in a logical statement. onCreate() is only called when the Activity is created. So if you start your SignUp Activity and then press the back button, there won't be a layout for your HomeActivity.
edit: And like Shark said it doesn't look like you have a way to pass the data from SignUp back to HomeActivity. You should try to use startActivityForResult() and then use setResult() in SignUp to say whether it was successful or not.
You need to let onCreate() finish, then move your new Activity to onResume() or onStart(); those get called as your activity is shown to the user instead of it still initializing (itself).
Also, it looks like you need to set up a
onLoginFinished(int result);
callback... either try running your login with startActivityForResult() and catching the result in onActivityResult()
OR ...
you make your own interface and once the other activity finishes it, it invokes the onLoginFinished(isOK) callback where you determine which screen to use.