Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the method onResume is going to be executed twice if the app is still running the onCreate method?

I am developping an Android app and in one of the activities I am using the MapsActivity to show a Map. What I have seen is that on an 2.2 (API8) emulator it takes some time to load the map and I have time to press the menu button and then come back to the app and it is still loading on the setContentView(), the problem comes when it goes to onResume() which is gonna be called twice.

According to the lifecycle of an Android activity, after a onPause()->[onRestart()-->onStart()]-->onResume() is gonna be called if the app comes to the foreground again, and onResume() is called after onCreate()->[onStart()] when is launched.

But why is not called once if the it is still loading on setContentView in onCreate() ?

This is something that interest me because I would not like to use a boolean each time I use maps thinking that it could be executed twice to avoid problems, i.e a double incrementation of a counter.

I do not know if it is a problem of the emulator as the issue I have seen about landscape-portrait orientation http://code.google.com/p/android/issues/detail?id=2423.

Please take a look at this:

  public class LocationActivity extends MapActivity {

    private static final String TAG = "LocationActivity";
    private static int i;

    protected void onCreate(Bundle savedInstanceState) {
       i = 0;
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_location);
    }

    protected void onResume(){
       super.onResume();    
       i++;
       Log.w(TAG,String.valueOf(i));           
       showDialogSettings();
    }

    private void showDialogSettings() {

      AlertDialog.Builder dialog = new AlertDialog.Builder(this);
      String title = "I-Value:" + String.valueOf(i);
      String positiveButton = "OK";
      final Intent intent = new Intent(Settings.ACTION_SETTINGS);

      dialog.setTitle(title);
      dialog.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {   
            Intent settingsIntent = intent;
           startActivity(settingsIntent);
         }
      });
      dialog.show();
   }

   @Override
   protected boolean isRouteDisplayed() {
      return false;
   }
  }

activity_location.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <com.google.android.maps.MapView
            android:id="@+id/locationactivity"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:apiKey="XXXXXXXXXXXXXXX"
            android:clickable="false"
            android:enabled="true" />

    </LinearLayout>

You can re-create the issue:

  1. If you set a breakpoint at setContentView an another one at super.OnResume().
  2. Execute and when the debug view.
  3. Send to background the app and re-run it again.
  4. Finish the execution , you should see a dialog showing a value:2.


Read the comments done by Geobits and G. Blake Meike, this part is an answer just to clarify if I am wrong.

Maybe the example with maps was a bad one because of the asyncronous load of the map. I changed the MapsActivity for an Activity, let's suppose that the phone is overloaded, so onCreate is gonna execute a loop of 8 seconds (not enougth time for an Android Not Responding).

And here the new code using a light android layout:

 public class LocationActivity extends Activity {

private static final String TAG = "LocationActivity";
private static int i;

protected void onCreate(Bundle savedInstanceState) {
    i = 0;
    Log.w(TAG,"onCreate");
    super.onCreate(savedInstanceState);
    setContentView(android.R.layout.simple_spinner_item);
    Log.w(TAG,"beforeLoop");
    try {
        for(int j = 0; j < 8; j++){
            Log.w(TAG,"Sleeping...");
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
    }
    Log.w(TAG,"afterLoop");
}

protected void onResume(){
    super.onResume();
    Log.w(TAG,"onResume" + String.valueOf(i));
    i++;
    Log.w(TAG,"check Mobile Connectivity");     
    ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();       
    if(networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_MOBILE && networkInfo.isAvailable()){
        Toast.makeText(this, "Network available!", Toast.LENGTH_LONG).show();
        Log.w(TAG,"showingToast");
    }
    
}

protected void onPause(){
    super.onPause();
    Log.w(TAG,"onPause:" + String.valueOf(i));
}

protected void onStop(){
    super.onResume();
    Log.w(TAG,"onStop:" + String.valueOf(i));
}
}

If I minimize the app when the log is still printing "Sleeping..." and quickly I re-run the app I can see the "Sleeping...", onResume will check two times the Connectivity (this is a correct way of checking the network connectivity).

Here goes the logCat:

  • 2-28 20:02:48.643: W/LocationActivity(651): onCreate
  • 2-28 20:02:48.646: W/LocationActivity(651): beforeLoop
  • 02-28 20:02:48.646: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:49.655: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:50.678: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:51.673: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:52.674: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:53.738: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:54.773: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:55.795: W/LocationActivity(651): Sleeping...
  • 02-28 20:02:56.816: W/LocationActivity(651): afterLoop
  • 02-28 20:02:56.824: W/LocationActivity(651): onResume0
  • 02-28 20:02:56.824: W/LocationActivity(651): check Mobile Connectivity
  • 02-28 20:02:57.134: W/LocationActivity(651): showingToast
  • 02-28 20:02:57.234: W/LocationActivity(651): onPause:1
  • 02-28 20:02:57.253: W/LocationActivity(651): onStop:1
  • 02-28 20:02:57.264: W/LocationActivity(651): onResume1
  • 02-28 20:02:57.264: W/LocationActivity(651): check Mobile Connectivity
  • 02-28 20:02:57.324: W/LocationActivity(651): showingToast

Toast will show two times the message.

Looking at the logCat the lifecycle is correct, but I just want to take into consideration that sometimes onCreate could be delayed because of an overloaded system and if onResume is executed twice then I have to take care of some initializations, so I have to use booleans which I consider I should not use because the onCreate was still running.

If instead of a Toast it is a dialog, from the POV of the user two dialogs are not welcome.

Please execute the code in the same way as I do, I am stubborn enough to not give up :P

like image 667
AlexBcn Avatar asked Feb 28 '13 15:02

AlexBcn


People also ask

Why is onResume called after onCreate?

onResume() will never be called before onCreate() . Show activity on this post. onResume() will always be called when the activity goes into foreground, but it will never be executed before onCreate() .

How many times onResume is called?

onResume() is triggered twice, but when I remove requestWindowFeature() , it's only called once.

What is the use of onResume method in Android?

onResume() When the activity enters the Resumed state, it comes to the foreground, and then the system invokes the onResume() callback. This is the state in which the app interacts with the user. The app stays in this state until something happens to take focus away from the app.

What is onResume ()?

onResume() is called whenever you navigate back to the activity from a call or something else. You can override the onResume method similarly as onCreate() and perform the task.


1 Answers

This is by design. If you send the activity to background, it must call onResume() when it returns.

From the documentation:

Be aware that the system calls this method every time your activity comes into the foreground, including when it's created for the first time. As such, you should implement onResume() to initialize components that you release during onPause() and perform any other initializations that must occur each time the activity enters the Resumed state (such as begin animations and initialize components only used while the activity has user focus).

Also note, setContentView() has probably already returned. It shouldn't take long, even for a MapView. The map is probably loaded asynchronously, so it doesn't tie up the UI thread.

like image 76
Geobits Avatar answered Nov 15 '22 05:11

Geobits