So im having some trouble restoring the state of my Activity.
At this point I figure that its probably a problem with my understanding rather then anything else.
My goal is to move from my MainActivity to a Main2Activity.
Am I correct in thinking that when a user moves from one page to another, it should be done by changing Activity via Intent?
I am doing this like so:
The onCreate() for my MainActivity has this in it.
Button currentButton = (Button) findViewById(R.id.button2);
currentButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
setContentView(R.layout.activity_main2);
Intent nextIntent = new Intent(getApplicationContext(), Main2Activity.class);
startActivity(nextIntent);
}
}
);
Which as I understand should call onCreate(), onStart() and onResume() for Main2Activity, then onSaveInstanceState() for MainActivity, then onStop() for MainActivity.
Ive overloaded all those functions with logging and seen that indeed they are being called and in that order.
Here is my onSaveInstanceState() for MainActivity:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
System.out.println("onSaveInstanceState called, saving state");
savedInstanceState.putInt("mySuperUniqueKey", testInt);
super.onSaveInstanceState(savedInstanceState);
}
Once in Main2Activity, I return back to MainActivity in the same way. I.e. findViewById() the button, overload its onClickListener(), create a new Intent and start it.
Then MainActivity class's onCreate() has this :
if (savedInstanceState != null) {
System.out.println("savedInstanceState is not null");
testInt = savedInstanceState.getInt("mySuperUniqueKey");
} else {
System.out.println("savedInstanceState is null");
}
When returning back the MainActivity from Main2Activity, I can see from the logging that onCreate(), then onStart(), then onResume() for MainActivity is called, then onStop() for Main2Activity. Unfortunatly the logging shows that savedInstanceState always comes back as null.
To add to this, when in the emulator, switching the orientation back and forth causes this to work perfectly; savedInstanceState is not null and features the saved testInt.
Thus I figure its a problem with my understanding and that there must be something im missing.
My gradle has minSdkVersion set to 16, and targetSdkVersion set to 28. Am I maybe targeting too low a minSdkVersion?
I have read through the "Understand the Activity Lifecycle" on the official android developer documentation but still cant get it.
https://developer.android.com/guide/components/activities/activity-lifecycle
I did find similar problems but none of them match my situation exaclty, also the solutions they have suggested I am already doing anyway.
Any insight would be greatly appreciated, thanks in advance.
The saved instance state bundle is intended to save the state of the current activity across things like orientation changes. It is not designed to persist across activities. You should use Intent#putExtra:
nextIntent.putExtra("mySuperUniqueKey", testInt);
Then, in your next activity, access this passed value using:
int testInt = getIntent().getIntExtra("mySuperUniqueKey");
My program runs an activity on startup, afterwards it sends an Intent opening another activity. The first time this happens, I want to save the information from the Intent to the savedInstanceState so whenever the app is opened again that information is available. The code looks like this:
savedInstanceState.putString("name", getIntent().getStringExtra("name"));
savedInstanceState.putString("font", getIntent().getStringExtra("font"));
savedInstanceState.putInt("background", getIntent().getIntExtra("background", R.drawable.bg1big));
However I continue to get a NullPointerException saying
Attempt to invoke virtual method 'void android.os.Bundle.putString(java.lang.String, java.lang.String)' on a null object reference.
You basically need to overwrite onSaveInstanceState() and onRestoreInstanceState() to store and retrieve the values that you want. Have a look at this answer - https://stackoverflow.com/a/151940/1649353
savedInstanceState is what you were given if something was saved last time - it's what was reloaded. even if it isn't null, you shouldn't add anything to it, it won't persist. Do this instead:
#Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putString("name", mName);
bundle.putString("font", mFont);
bundle.putInt("background", mBackground);
}
And then in onCreate and onRestoreInstanceState you can read the values and populate those variables you'll later save
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mName = savedInstanceState.getString("name");
mBackground = savedInstanceState.getString("background");
mFont = savedInstanceState.getString("font");
}
I've got two activities, A and B. Activity B can be opened by pressing a button in Activity A.
In activity B I have an integer variable which I would like to keep for when I return to activity B from A. My problem is when I press the back button to go from B to A the activity is destroyed.
I have overwritten the onBackPressed method to:
#Override
public void onBackPressed(){
Intent i = new Intent(this, Game.class);
startActivity(i);
}
I can see from my logs that activity B is in the state onStop() after back button is pressed, however, onRestart() is not being called so the activity must be getting killed for memory reasons.
I have read answers to other posts suggesting I use onSaveInstanceState() but when I try to access the bundle in onCreate() the bundle is null. Method onRestoreInstanceState() does not get called.
protected void onSaveInstanceState(Bundle savedInstanceState){
Log.i(LOG, "instance saving");
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("score", userScore);
}
I have also tried SharedPreferences but this is not useful because I do not want my data to persist when the activity/application is intentionally destroyed.
I think your problem is in understanding the whole Task ecosystem. When you press back button you pop out your activity from the Task, because of that it is destroyed and onDestroyed() is called. To sum-up I think you are just getting every time a brand new activity. onSaveInstanceState() isn't called because activity is killed by user, not by the OS.
Take a deeper look at this developer tutorial.
Also I think those two must be helpful : me and me!
you can store variables in the app class https://developer.android.com/reference/android/app/Application.html or you can make your own singleton class for this
https://www.tutorialspoint.com/design_pattern/singleton_pattern.htm
Starting a Activity - A on onBackPressed will definitely kill the current Activity - B. Instead of starting Activity again just call onBackPressed in Activity - B and add a stage called onResume() which is called when you resume back to Activity B from A
Remove this:
#Override
public void onBackPressed(){
Intent i = new Intent(this, Game.class);
startActivity(i);
}
With this:
#Override
public void onBackPressed(){
super.onBackPressed();
}
When you coming back from A to B, in B #Override stage onResume() and in this you can save the value while coming back from Activity A.
Add this in Activity B:
#Override
protected void onResume() {
super.onResume();
// save values here for resume
}
Look the Activity Life Cycle:
I'm having some issues with the onSaveInstanceState, but it may be because I misunderstand how it works.
I have a Arraylist i want to save when I switch activities then restore when i come back.
so i have this method
#Override
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putStringArrayList("History", History);
and my onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if( savedInstanceState != null ) {
History = savedInstanceState.getStringArrayList("History");
}
}
For some reason this loop is not being initliased
if( savedInstanceState != null ) {
History = savedInstanceState.getStringArrayList("History");
}
I'm pretty sure its soemthing fairly simple but I'm still quite new to Java.
If you are simply switching activities, it will stay 'alive' in the background (unless the OS decides to kill it off to free memory). Read this article for more information on Activity state
In this case, onCreate() will not be called when the Activity resumes, as it has already been created and is simply been onResume()-ed.
You should look at another method of storing your History values such as in SharedPreferences, a database or in a persistent object on the application class instance (whatever suits your preference) in your onPause() method and restore those values in onResume().
onSavedInstanceState() is used for when an activity is killed like in an orientation change or OS kill.
Basically, this is what I'm doing
1) Set AlarmManager to execute BroadcastReceiver (BCR)
Intent intent = new Intent(m_Context, BCR.class);
intent.putExtras(extras);
PendingIntent pendingIntent = PendingIntent.getBroadcast(m_Context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, StartTime, pendingIntent)
2) Start MyActivity from BCR
#Override
public void onReceive(Context context, Intent intent) {
Intent newIntent = new Intent(context, MyActivity.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(newIntent);
}
3) Have MyActivity turn on the screen if its not on
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(LayoutParams.FLAG_DISMISS_KEYGUARD);
getWindow().addFlags(LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().addFlags(LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.myactivity);
}
#Overide
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
For some reason, I notice that right when MyActivity is opened, it's flow goes like:
onCreate/onNewIntent -> onResume -> onPause -> onResume
I'm not sure why it does an onPause right away. I notice this only happens when the screened is being turned on by the flags. Does anyone know why this happens? Is there any way this behavior can be prevented?
if you trying request permissions every time it can cause such problems, just check if you already granted them
requestPermissions can cause it:
onCreate
onStart
onResume
onPause
onResume
Use this method ContextCompat.checkSelfPermission(context, permission) to check if permission was granted or not before requesting it
This method returns int and you can check it with PackageManager.PERMISSION_GRANTED constant
Just in case anyone else runs into this, I seem to notice this behaviour only when I inflate fragments inside of an activity via XML layout. I don't know if this behaviour also happens with the compatibility library version of Fragments (I'm using android.app.Fragment)
It seems the activity will call Activity#onResume once before calling Fragment#onResume to any added Fragments, and then will call Activity#onResume again.
Activity:onCreate
Fragment:onAttach
Activity:onAttachFragments
Fragment:onCreate
Activity: onStart
Activity: onResume
Fragment: onResume
Activity: onResume
If you have ES File Explorer then FORCE STOP it. Somehow, they interrupt your app's lifecycle (comments suggest some kind of overlay).
My issue with onResume being caused twice was because onPause was somehow being called after the activity was created.. something was interrupting my app.
And this only happens after being opened for the first time after installation or built from studio.
I got the clue from another post and found out it was because of ES File Explorer. Why does onResume() seem to be called twice?
As soon as I force stop ES File Explorer, this hiccup behavior no longer happens... it's frustrating to know after trying many other proposed solutions. So beware of any other interrupting apps like this one.
I was researching about this for a while because on the internet there is no any mention about this weird behaviour. I don't have a solution how to overcome this dark-side-behavior but I have found an exact scenario when it certainly happens.
onPause-onResume-onPause-onResume just happens every time, when app is starting first time after installation. You can simply invoke this behavior by doing any change in code and rerunning (which includes recompiling) the app from your IDE.
No matter if you use AppCompat libs or not. I have tested both cases and behavior carries on.
Note: Tested on Android Marshmallow.
I have borrowed the code from this thread about fragment and activity lifecycle and here it is (just copy, paste, declare activity in manifest and run Forest run):
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class TestActivity extends Activity {
private static final String TAG = "ACTIVITY";
public TestActivity() {
super();
Log.d(TAG, this + ": this()");
}
protected void finalize() throws Throwable {
super.finalize();
Log.d(TAG, this + ": finalize()");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");
TextView tv = new TextView(this);
tv.setText("Hello world");
setContentView(tv);
if (getFragmentManager().findFragmentByTag("test_fragment") == null) {
Log.d(TAG, this + ": Existing fragment not found.");
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(new TestFragment(), "test_fragment").commit();
} else {
Log.d(TAG, this + ": Existing fragment found.");
}
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, this + ": onStart()");
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, this + ": onResume()");
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, this + ": onPause()");
}
#Override
public void onStop() {
super.onStop();
Log.d(TAG, this + ": onStop()");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}
public static class TestFragment extends Fragment {
private static final String TAG = "FRAGMENT";
public TestFragment() {
super();
Log.d(TAG, this + ": this() " + this);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, this + ": onCreate()");
}
#Override
public void onAttach(final Context context) {
super.onAttach(context);
Log.d(TAG, this + ": onAttach(" + context + ")");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, this + ": onActivityCreated()");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, this + ": onCreateView()");
return null;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, this + ": onViewCreated()");
}
#Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, this + ": onDestroyView()");
}
#Override
public void onDetach() {
super.onDetach();
Log.d(TAG, this + ": onDetach()");
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, this + ": onStart()");
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, this + ": onResume()");
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, this + ": onPause()");
}
#Override
public void onStop() {
super.onStop();
Log.d(TAG, this + ": onStop()");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, this + ": onDestroy()");
}
}
}
I don't know for sure what's going on, but I suspect that your activity is being restarted because setting the screen on is treated by the system as a configuration change. You might try logging the configuration on each call to onResume to see if that's what's happening and, if so, what is actually changing. You can then modify the manifest to tell the system that your activity will handle the change on its own.
protected void onResume() [
super.onResume();
Configuration config = new Configuration();
config.setToDefaults();
Log.d("Config", config.toString());
. . .
}
I have similar problem.
My situation was next
CurrentActivity extends MainActivity
CurrentFragment extends MainFragment
I was opening CurrentActivity with intent as usually. In onCreate CurrentAcitivity I was replacing CurrentFragment.
Life Cycle was:
1. onResume MainActivity
2. onResume CurrentActivity
3. onResume MainFragment
4. onResume CurrentFragment
called onPause Automatically, and after that again
onResume MainActivity
onResume CurrentActivity
onResume MainFragment
onResume CurrentFragment
I decide to retest everything and after few hours spend trying and playing I found root issue.
In MainFragment onStart I was calling startActivityForResult every time (in my case android popup for turning on Wifi) which was call onPause on MainFragment. And all of us know that after onPause next is onResume.
So its not Android bug, it's only mine :-)
Happy lifecycle debuging!
I also ran into this onresume-onpause-onresume sequence (on 4.1.2 and above, but I did not experience this on 2.3). My problem was related to wakelock handling: I accidentally forgot to release a wakelock and reacquiring it caused an error with a message "WakeLock finalized while still held". This problem resulted in onPause being called immediately after onResume and resulted in faulty behavior.
My suggestion is: check for errors in the log, those might be related to this issue.
Another hint: turning on the screen might be a bit more tricky than simply using window flags. You might want to check this answer here - it suggests you set up a receiver to check if the screen has already been turned on and launch the desired activity only after: https://stackoverflow.com/a/16346369/875442
I had a similar issue, and my problem was that at the onCreate() method, I was doing:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.setContentView(R.layout.friends); <-- problem
}
My call to "super." was triggering the onResume() twice. It worked as intended after I changed it to just:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.friends); <-- 'super.' removed
}
Hope it helps.
Have you tried calling your getWindow().addFlags(...) before calling super.onCreate(savedInstanceState) in onCreate method?
I had a similar problem. onResume was called twice when my onCreate looked like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setContentView(R.layout.main);
...
}
Changing it to:
#Override
public void onCreate(Bundle savedInstanceState) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
}
... fixed the problem.
It seems that using Activity from the support library saves and restores instance automatically. Therefore, only do your work if savedInstanceState is null.
I just ran into this, and it seems that getWindow().addFlags() and tweaking Window properties in general might be a culprit.
When my code is like this
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_generic_fragment_host);
// performing fragment transaction when instance state is null...
onResume() is triggered twice, but when I remove requestWindowFeature(), it's only called once.
I think you should have a look at that question:
Nexus 5 going to sleep mode makes activity life cycle buggy
You should find leads
Basically a lot of stuff can trigger this. Some resume processes that loses focus can do it. A few apps will cause it to happen too. The only way to cope is to block the double running. Note, this will also have an errant pause thrown in for good measure.
boolean resumeblock = false;
#Override
protected void onResume() {
super.onResume();
sceneView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
sceneView.getViewTreeObserver().removeOnPreDrawListener(this);
if (resumeblock) return false;
resumeblock = true;
//Some code.
return false;
}
});
}
This is a solid way to prevent such things. It will block double resumes. But, it will also block two resumes that preserve the memory. So if you just lost focus and it doesn't need to rebuild your stuff. It will block that too. Which might be a benefit clearly, since if you're using the resume to control some changes over focus, you only actually care if you need to rebuild that stuff because of focus. Since the pre-draw listeners can only be called by the one thread and they must be called in sequence, the code here will only run once. Until something properly destroys the entire activity and sets resumeblock back to false.
as #TWL said
ES File Explorer
was the issue for me !
Uninstalling the app solved the problem.
When this ES File Explorer was installed, onStart() -> onResume() -> onPause() -> onResume() .. was the problem.
onResume() was called 2'ce.
I had the same problem. Mine was for this code in runtime
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
I just put it in manifest
android:screenOrientation="landscape"
no more problem about twice call to onCreate and onResume.
I didn't see an adequate answer to the question, so I decided this.
From My Notes:
"It seems that the activity runs onResume() onPause() then onResume again. The very last method that runs is the onSizeChanged(). So a good practice would be to start threads only after the onSizeChanged() method has been executed."
So you could: Make a log of each method that runs. Determine the last method that runs. Ensure that you have a Boolean that initializes false, and only changes to true after your last method runs. Then you can start all threading operations, once you check that the Boolean is true.
-For anyone wondering: I am using a surfaceview that has a onSizeChanged() method that executes very last.
I had the same problem because of setting the UiMode in the onCreate() of MainActivity. Changing the theme triggered activity recreation and made two calls to onPause() and onStart().
I was sure this was happening in my app until I realized I had planted two of Jake Wharton's Timber trees, and onResume() was just being logged twice, not called twice.
i also faced this issue this is because of fragments..
the number of fragments you have in activity onResume() will call that number of times. to overcome i used flag variables in SharedPrefrences