onCreate in an Activity ignores all the call stack sequence - java

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

Related

How do I determine if setPersistenceEnabled is already enabled?

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.

Run App Twice To Work

I'm making an android app that test if certain security features on your phone are enabled. For example, if you have password log in enabled or if your data is encrypted on your phone.
For some reason, the app has to be run twice to test and see if these security features are enabled on the phone or not, and this is the problem I'm trying to solve. I'd like it to test and see if the security features are enabled when the app is created and the first time the app is run, not the second time it is run.
I test if these features are enabled in the onStart() function in my MainActivity file. I included the functions code below:
#Override
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#SuppressLint("NewApi")
public void onStart()
{
super.onStart();
//determine if phone uses lock pattern
//It returns 1 if pattern lock enabled and 0 if pin/password password enabled
ContentResolver cr = getBaseContext().getContentResolver();
lockPatternEnable = Settings.Secure.getInt(cr, Settings.Secure.LOCK_PATTERN_ENABLED, 0);//Settings.System
//returns 1 if pin/password protected. 0 if not
KeyguardManager keyguardManager = (KeyguardManager) getBaseContext().getSystemService(Context.KEYGUARD_SERVICE);
if( keyguardManager.isKeyguardSecure())
{
//it is pin or password protected
pinPasswordEnable=1;
}
else
{
//it is not pin or password protected
pinPasswordEnable=0;
}//http://stackoverflow.com/questions/6588969/device-password-in-android-is-existing-or-not/18716253#18716253
//determine if adb is enabled. works
adb=Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0);
//determine if bluetooth is enabled.works
bluetooth=Settings.Global.getInt(cr, Settings.Global.BLUETOOTH_ON, 0);
//Settings.System BLUETOOTH_DISCOVERABILITY
//determine if wifi is enabled. works
WifiManager wifi = (WifiManager)getSystemService(Context.WIFI_SERVICE);
if (wifi.isWifiEnabled())
{
//wifi is enabled
wifiInt=1;
}
else
wifiInt=0;
//determine if data is encrypted
getDeviceEncryptionencryption();
//determine if gps enabled
}//end of onStart() function
If any more code needs to be posted to answer this question, just let me know, and thanks for your help. Maybe the issue has something to do with the super.onStart();
Does anyone think that a splash loading screen might help solve the issue?
Here is good explanation how app lifecycle flows.
onStart() can be executed many times. You can keep counter how many times you had entered this method and act differently on each time:
static int counter=0;
public void onStart()
{
counter++;
Log.i("MyApp", "onStart() run "+counter);
switch (counter){
case 1: break; // first run
case 2: break; // second run
default: break;// other runs
}
}
To be more clear about life cycle and why your onStart() method is called twice I suggest to have counter and Log.i() in each important state of the cycle - at least in onCreate() and onRestart().
Keep in mind that app stays in memory when you click Home button. When you click app icon again it restarts already running app (calls onRestart() and then onStart() methods and no onCreate() ). When you really kill you app for real then sequence would be onCreate and onStart without onRestart. Having logcat records really helps you to understand app lifecycle flow and why your onStart() is called twice or more times.
Using a static variable to check how many times onStart has been called isn't a good idea, because an app can be killed if Android needs more memory for other apps while still allowing the user to navigate back to the app. That would be the path through the red box in the picture below (http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle):
A static variable would be 0 again after that and your app would run the security check again.
What you need to do is use an instance variable that you persist in onSaveInstanceState and restore in onCreate. In case the app is killed, onSaveInstanceState is called and you save your Activity's state. If the user goes back to the app, onCreate is called and the state would be restored. This works for all other cases too when the app isn't killed but the user just navigates away from the app and later re-opens it. Here's a simple example of an app saving and restoring:
public class MainActivity extends Activity {
private boolean mSecurityCheckDone;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mSecurityCheckDone = savedInstanceState.getBoolean("mSecurityCheckDone");
}
}
#Override
protected void onStart() {
super.onStart();
if (! mSecurityCheckDone) {
// run the security check
mSecurityCheckDone = true;
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("mSecurityCheckDone", mSecurityCheckDone);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
mSecurityCheckDone = savedInstanceState.getBoolean("mSecurityCheckDone");
}
}
}
how about using flag ?
singleTop
If an instance of the activity already exists at the top of the target
task, the system routes the intent to that instance through a call to
its onNewIntent() method, rather than creating a new instance of the
activity.
singleTask
The system creates the activity at the root of a new task and routes
the intent to it. However, if an instance of the activity already
exists, the system routes the intent to existing instance through a
call to its onNewIntent() method, rather than creating a new one.
singleInstance
Same as "singleTask", except that the system doesn't launch any other
activities into the task holding the instance. The activity is always
the single and only member of its task.
http://developer.android.com/guide/topics/manifest/activity-element.html
I'm not sure why you are using onStart(), if you want it to be run the first time the activity is created I would probably use onCreate() instead.
There is no API in Android that will tell you if the app has been run at least once so you will need to use some type of persistent storage for that, e.g. SharedPreferences could be used to persist a flag that would be set the first time your app is run and thereafter you can check it as shown here.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences settings = getSharedPreferences("Prefs", 0);
if (settings.getBoolean("first_time", true)) {
//the app is being launched for first time, do something
Log.d("Comments", "First time");
// first time task
// record the fact that the app has been started at least once
settings.edit().putBoolean("first_time", false).commit();
}
}

Android Trying to display a welcome fragment when app starts, but not after

I've been looking for a way to have the blank detail side of my fragment layout host a welcome screen (or something - login perhaps) on start up. Afterwards, when a user presses one of the left side menu items, I'd like to eliminate the fragment for the remainder of the program run. I don't want to add it to the backstack, as that messes up my configuration changes. I've considered using shared prefs to host a boolean about whether the fragment has been displayed. The only concern with this method is where to safely reset the boolean value for the next run of the app. I'm of the impression that there's no gaurantee that the onStop, onDetach etc. will definitely get called upon closing of the app, so if the app got closed in the wrong state, it would be rendered useless ( the first fragment wouldn't display - crash )
Anyone have any ideas on how I could implement a filler for the right side of the app upon startup?
I've been trying to add something to the onCreate of my main activity thus far with no success.
Thanks in advance.
Ken
If your fragment can be part of its own Activity, you can use the android:noHistory="true" attribute to keep the Activity off of the backstack. If your user tries to navigate backwards, it'll hit the bottom of the backstack twice before exiting your application.
If you can't split your fragment into its own activity, noHistory may not work -- I can't say as I haven't tried it myself.
I was able to come up with a solution to creating a welcome or login screen which will display both fragments and activities from the main activity. Seems to be working fine as tested.
private boolean welcomeShown;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_list);
if (findViewById(R.id.item_detail_container) != null) {
mTwoPane = true;
((MainFragment) getSupportFragmentManager().findFragmentById(
R.id.item_list)).setActivateOnItemClick(true);
}
if (savedInstanceState != null){
welcomeShown = savedInstanceState.getBoolean("displayed");
}
if(!welcomeShown){
if (mTwoPane){
WelcomeFragment welcomeFragment = new WelcomeFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.item_detail_container, welcomeFragment)
.commit();
}
else{
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivity(welcomeIntent);
welcomeShown = true;
}
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("displayed", true);
}

Set starting Activity with code in Android App

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.

Android: Starting an intent inside of onCreate is causing an infinite loop / crash

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.

Categories