I have a synchronicity problem. I have searched and cannot find a question with the same conditions as mine. Pointers are welcome.
Android 4.1.2 on Eclipse 4.2.1
I have two activities; let's call them ListActivity and DetailActivity. On a button press on ListActivity, I start DetailActivity. When the user makes changes to data in the DetailActivity and then clicks the back button, I want to do two things. First, I want to save changes to the database if a dirty flag is true. Second, I want to return an Intent to the onActivityResult() method of the ListActivity.
The problem is one of timing. Since I want to do database updating, I read that it is best practices to do this in the onStop() method of the DetailActivity. Right after I update the database I set up the Intent and call the setReult() method in the DetailActivity.
_resultIntent = new Intent();
_resultIntent.putExtra(ListActivity .DETAIL_RESULT, _currentData);
Log.d("ListActivity.setUpReturn()", "checkpoint");
setResult(Activity.RESULT_OK, _resultIntent);
The onActivityResult() method of the ListActivity is called before I have a chance to set the result. I placed Log calls throughout and what appears to happen is the following:
1) The user is in the DetailActivity and changes some data setting the dirty flag to true.
2) The user clicks the system back button.
3) The DetailActivity disappears from the screen.
4) The ListActivity re-appears.
5) The Log notes the onActivityResult(), onRestart(), onStart(), and onResume() methods of ListActivity execute in that order
6) Some other stuff is logged.
7) The Log FINALLY notes that the onStop() method of the DetailActiviy executes.
This means that by the time I am updating the database and setting up the result Intent, the calling activity has already gone past the point of using it.
I'd rather not constantly update the database. I'd rather put this into the onStop() method like the best practices says, but I don't know how to get around the issue of timing. Any suggestions? Am I doing something obviously wrong?
Check that did you make your activity as a SingleInstance if yes then make it to SingleTop
I would rather rely on event based code logic rather than on probability. You could try the following.
*1) The user is in the DetailActivity and changes some data setting the dirty flag to true.
2) The user clicks the system back button.*
Override the onBackPressed method and perform your database operation right there and once the DB operation is performed you can call the super.onBackPressed() method and let the Android Activity stack take over.
Mind you DB operation could be time consuming and hence it is also recommended to use AsyncTask with callback and only then call finish().
That's because onPause() is called right after the activity loses focus. The onStop() however is called when the activity is going to be destroyd (usually when there is not enough memory). So put your code into the onPause() method and you should be fine...
It turns out the best solution for me was from PravinCG. I will use this as a pattern in the future.
#Override
public void onBackPressed() {
Log.d("DetailActivity.onBackPressed()", "checkpoint");
if (_currentDataIsDirty) {
DoDatabaseWork(_currentData);
}
_resultIntent = new Intent();
_resultIntent.putExtra(ListActivity .DETAIL_RESULT, _currentData);
setResult(Activity.RESULT_OK, _resultIntent);
super.onBackPressed();
}
Dogulas
Related
I read the Android documentation and I don't understand one step:
When I press a button my app shows the Activity2 with the startActivity(intent) method, then I use the back button and my app shows the Activity1 again. If I want show the Activity2 I press the button again, and my app always call onCreate to the Activity2.
The Android documentation says the method onCreate is called only when is starting or when is destroyed.
Why is this happening?
Thanks!!
Regars.
The OnCreate() method is called each time the activity is displayed (created). So each time you call the startActivity(intent) method, the OnCreate method will be called.
Check the Activity Lifecycle for more information.
It's because you pressed the back button when you were in Activity2, which by default destroys the activity you're currently on. You can override onDestroy() and print a debug message to confirm (make sure to call super).
Instead of retaining the same Activity2 object, you should leverage onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle) to save and restore your Activity2's state, respectively.
Technically you could use the Bundle object passed into onCreate(Bundle) as they are the same object. The docs recommend onRestoreInstanceState(Bundle):
Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation.
It's completely normal beheviour. You are calling startActivity() so activity is starting. That's all. This method is called also when you are changing configuration - i.e rotating the device. Moreover - it's also possible that Activity1.onCreate() will be called after pressing back button, while it's going background and can be disposed by system if more ram is needed.
I have a problem with Activity life cycle. In my server communication Activity I am downloading the list of items from the server and then setting up the Adapter for the ListView.
Everything is fine but if I press Home button on this screen and after some while (e.g. 3 hours or more) return back to the screen via application manager, application crashes. The problem is in the onTextChanged() method (which is usefull for searching via EditText) where I am calling the setAdapter() method again. There is nullPointerException because my array was somehow erased.
Why is the onTextChanged() method called again during restoring? And why was the array erased?
Thank you for your help.
Please check the activity lifecycle diagram on:
https://developer.android.com/reference/android/app/Activity.html
Your Activity goes to Pause state after you press Home.
And after 3hrs or even a shorter time,it may be killed by system for resource management.
So it need to be created again on next launch.
I think you should add code to handle onDestroy() and onStop().
Retrieve the data the same way you did originally, by overriding the
onResume()
method, checking if the data is existent or not beforehand.
I am creating a simple application that entails multiple activities. It is necessary for my application to resume one of two activities based on a certain system preference. Because of this, I have implemented a Dispatcher class that is linked to action.MAIN, that then starts the correct Activity by examining the system preferences within its onResume() callback.
The Problem
Since I am doing this in onResume(), when the back button is pressed on any of the resulting activities, the Dispatcher is resumed, and as a result it again tries to start the same Activity again. This prevents the user from ever leaving the application unless they press the home button.
Some Code
Here is some code excerpts from my classes to help clarify my situation:
Dispatcher.onResume(void) : void
protected void onResume() {
super.onResume();
if(!handled){
Intent activity_switcher;
//If a game is active...
if(manager.isGameActive()){
//Start the the Game Manager with the appropriate game.
activity_switcher = new Intent(this, GameManager.class);
}
//If a game is not active...
else{
//Start the Game Menu
activity_switcher = new Intent(this, Menu.class);
}
//Start the appropriate activity
startActivity(activity_switcher);
overridePendingTransition(0, 0);
}
else{
//Finish the application
finish();
overridePendingTransition(0, 0);
}
}
Some of the variables are as follows:
manager is the variable representing my class that interfaces with the system preferences.
handled is the variable that determines if the event has been handled yet (see "Attempted Solution #1" below)
Attempted Solution #1
I have considered implementing a boolean class variable, handled, that is tested before the activities are started. If the value is found to be true, then the Dispatcher simple makes a call to finish(), however if the value is false, then the appropriate activity is started and handled is set to true so that when the application returns to this Activity, it will simply finish.
This will not work, because...
If the user pressed the home button on one of the resulting activities, then when they resume the application, they will be placed on the dispatcher activity, and the application will immediately finish because it thinks that it was handled (the variable is still true).
Attempted Solution #2
I tried to find some way to only run to the Dispatcher once per application life cycle. This makes sense because the user would never want to return to the Dispatcher as it is an Activity that should only be ran once at launch time. So, I included a counter that would be incremented each time onResume() was called, and reduced to 0 each time finish() was called.
This will not work, because...
Again, if the user presses the home button, then when the application is re-launched, the Dispatcher will think that it has already been run once (because there was no call to finish()), and will immediately finish again.
Attempted Solution #3
I figured that if I could somehow determine which activity was previously active, then I could dynamically react to the case where the user pressed the back button, and then and only then inform the Dispatcher that it should finish, then there would be no immediate finishes in the case when the user presses the home button.
This was accomplished by using a static function returningFromMenu() that, when called from the menu Activity, raised a flag in the Dispatcher class that informed the it to finish.
Attempted Solution #4 (Current Implementation)
Simply moving all of this activity switching code into onCreate(). This would enforce the "one time" nature of the Dispatcher.
This might work, because...
This will handle the case where the user returns to the activity (using onResume()), and, since there will be no flags, there is no issue when returning to the application after pressing the home button.
My Question
Is there any way to determine which Activity was last finished, other than having to use a static method or member? I know that this is a very bad practice and would like to avoid it if possible.
Updates
Last Updated: 12 Feb 2013 3:45PM EST
Approach #4 seems to be working for my purposes, however I still think that the question here could be of great use to developers, and will refrain from answering my question until I have given enough time for a more holistic solution to be posted.
The Solution
As is stated in "Updates" above, this problem was solved by simply moving the code into onCreate() so that the switch was made only once. Equivalently, I could have simply made a call to finish() immediately after starting the activity in order to remove the calling activity from the back stack.
Thank you all for your efforts in the comment section!
I'm calling an Intent from an activity: I want to know what happens with the activity when I'm calling the Intent, I mean, is it destroyed? onPause? onStop?
This is what I use to call an Intent:
Intent intent = new Intent(context,class);
context.startActivity(intent);
I want to know that if I have a checkbox in an activity, so for example I check that checkbox and after I go to the next activity, but if I go back to the previous activity, the checkbox is not checked as it was when I call the Intent.
I don't know if I have explained my self, but I hope you can give me a hint to solve this.
By default, the activity is stopped, not destroyed. It might be destroyed if the system is low on resources.
So what's probably happening in your case is, the system gets low on resource so it destroys your activity. You should save your UI state in onSaveInstaceState, and restore it in onRestoreInstanceState. Read more here.
intent.putExtra("",""); Save chackbox state here
Put state of checkbox in shared preferences or static variable and once you go back to that activity , check for its state and populate it if that is the case
I mean, is it destroyed? onPause? onStop?
onStop is called.
From developer.android.com
onStop() - Called when the activity is no longer visible to the user.
Try saving the state of checkbox in static boolean with isChecked() and check that boolean in onRestart() - this is called, when you come back to your previous activity.
There are numerous prior questions on saving the state of a checkbox (whether due to intent or screen rotation). The same generate approach applies:
For instance:
How can I maintain the state of a CheckBox even after navigating to different activities in android?
And to determine the lifecycle calls being made, one can always override the methods with a call to super class and use breakpoints in the debugger. That has always been helpful to me to see the code in motion alongside the Android lifecycle diagrams.
so i've been trying to get my application to run an Activity via an intent and it works fine, when i then assign the finish(); method, it returns to the activity that called it. The only thing i don't understand is that i'm not sure if the callee Activity is put onPause while the called Activity is in-front. I've tried to setup a toast message in the onPause() method of the callee Acitivty but it won't appear.
I first tried to call the second Activity with startActivity(intentname) and then a finish() method on the first Acitivty, i then tried to use the startActivityForResult() (even though i don't really need to recieve any information from the called Activity) method and closed it with onActivityResult().
I can't find any information about the side-effects that these Activity methods has on a Activity that's calling another. So i'm wondering if anybody could help me out ?
//Thx in advance
According to the documentation for Activity, the onPause() lifecycle method WILL be called when another Activity is put in front of it.
http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
If the called Activity is is semi-transparent, then onStop() will also be called, but if your initial Activity is not visible at all, onStop() will not be called.
It is also of worth to note that when you call finish() on the called Activity, the onResume() will be called on the caller (and onStart(), assuming onStop() was also called)
To quickly answer your question: if activity A starts activity B, then A's onPause method is run. I think there might be an exception if B isn't full screen, but that's only a tentative memory from something I read in the documentation a while ago.
As for why your toast wasn't showing - did you remember to .show() it? I always used to forget to do that. Toasts can also get missed if they're triggered just as the activity is pausing, since its context goes away. There's a much easier way to test it - just use the Log method. For example, Log.d("My app name", "onPause was just triggered"); The purpose of the "My app name" string is to let you filter by it in LogCat. If you don't know how to display LogCat, and assuming you're using Eclipse, see this answer to another question.
got it to work , was a bit confused of the purpose with onResume(), i was suppose to decleare a onActivityResult() in the first Activity so that the second Activity would return to it right after finish()