Android Programming for Beginners
上QQ阅读APP看书,第一时间看更新

The lifecycle demonstration app

This quick experiment will help familiarize ourselves with the lifecycle methods our app uses, as well as give us a chance to play around with a bit more Java code:

  1. Start a new project and call it Lifecycle Demonstration. Of course, the code is in the download bundle in the Chapter 6/Lifecycle Demonstration folder should you wish to refer to it or copy and paste it.
  2. Accept the default target devices.
  3. Choose Blank Activity and don't worry about customizing the activity options at all.
  4. Open the MainActivity.java file in the code editor, if it is not opened for you by default, by left-clicking on the MainActivity tab above the editor.

Note

If the previous steps were not detailed enough, check back to any of the previous occasions when we created a new project for further details.

You have created a new project with all the settings on default. We will only need the MainActivity.java file for this demonstration and we will not be building a UI.

In the MainActivity.java file, find the onCreate method and add these two lines of code just before the closing curly brace }, which marks the end of the onCreate method:

Toast.makeText(this, "In onCreate", Toast.LENGTH_SHORT).show();
Log.i("info", "In onCreate");

The entire onCreate method should now look exactly like this, where the highlighted code is the two lines we just added:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

 Toast.makeText(this, "In onCreate", Toast.LENGTH_SHORT).show();
 Log.i("info", "In onCreate");
}

After the closing curly brace } of the onCreate method, leave one clear line and add the following five lifecycle methods and their contained code. Remember, you will need to use the Alt + Enter keyboard combination twice to import the classes needed for Toast and Log. Also note that it doesn't matter what order we add our overridden methods. Android will call them in the correct order regardless of the order we type them:

@Override
public void onStart() {
  // First call the "official" version of this method
  super.onStart();

  Toast.makeText(this, "In onStart", Toast.LENGTH_SHORT).show();
  Log.i("info", "In onStart");
}

@Override
public void onResume() {
  // First call the "official" version of this method
  super.onResume();

  Toast.makeText(this, "In onResume", Toast.LENGTH_SHORT).show();
  Log.i("info", "In onResume");
}

@Override
public void onPause() {
  // First call the "official" version of this method
  super.onPause();

  Toast.makeText(this, "In onPause", Toast.LENGTH_SHORT).show();
  Log.i("info", "In onPause");
}

@Override
public void onStop() {
  // First call the "official" version of this method
  super.onStop();

  Toast.makeText(this, "In onStop", Toast.LENGTH_SHORT).show();
  Log.i("info", "In onStop");
}

@Override
public void onDestroy() {
  // First call the "official" version of this method
  super.onDestroy();

  Toast.makeText(this, "In onDestroy", Toast.LENGTH_SHORT).show();
  Log.i("info", "In onDestroy");
}

First of all, let's talk about the code itself. Notice that the method names all correspond to the lifecycle methods and phases we have just discussed. Notice that all of the method declarations are preceded by the @Override line of code. Also see that the first line of code inside each method is super.on….

What it is that is going on here is this:

  • Android calls our methods at the various times we have already discussed.
  • The @Override keyword indicates that these methods replace/override the original version of the method that is provided as part of the Android API. Note we don't see these overridden methods but they are there, and if we didn't override them, these original versions would be called by Android instead of ours.
  • The super.on… syntax, which is the first line of code within each of the overridden methods, then calls these original versions.

So, we don't simply override these original methods in order to add our own code, we also call them and their code is executed too.

Tip

For the curious, the keyword super is for super-class. We will explore method overriding and super classes in several chapters as we progress.

Finally, the code that you added will make each of the methods output one Toast message and one Log message. However, the messages that are output vary, as can be seen by the text in between the double quote marks "". The messages that are output will make it plain which method produced them.

Now that we have looked at the code, we can play with our app and learn about the lifecycle from what happens:

  1. Run the app on either a device or an emulator.
  2. Watch the screen of the emulator and you will see the following appear, one after the other, as Toast messages on the screen: In onCreate, In onStart, and In onResume.
  3. Notice the following messages in the logcat window. If there are too many messages, remember you can filter them by setting the Log level drop-down to Info:
    info:in onCreate
    info:in onStart
    info:in onResume
  4. Now tap the back button on the emulator or the device. Notice you get the following three Toast messages in exactly this order. In onPause, In onStop, In onDestroy. Verify that we have matching output in the logcat window.
  5. Next, run a different app. Perhaps the Hello Android app from Chapter 1, The First App by tapping its icon on the emulator screen.
  6. Now try this: tap the task manager button. This is a square (as shown next) on the emulator but does vary a little on different devices:
  7. You should now see all the recently run apps on the device. Here is what my Nexus 5 emulator looks like at this point:
  8. Tap the Lifecycle Demonstration app and notice that the usual three starting messages are shown. This is because our app was previously destroyed.
  9. However, now tap the task manager button again and switch to the Hello Android app. Notice that this time, only the In onPause and In onStop messages are shown. Verify that we have matching output in the logcat. The app has not been destroyed.
  10. Now, again using the task manager button, switch to the Lifecycle Demonstration app. You will see that only the In onStart and In onResume messages were shown, indicating that onCreate was not required to get the app running again. This is as expected because the app was not previously destroyed, merely stopped.

Next let's talk about what we saw when we ran the app. When we started the Lifecycle Demonstration app for the first time, we saw that the onCreate, onStart, and onResume methods were called. Then, when we closed the app using the back button, the onPause, onStop, and onDestroy methods were called. Furthermore, we know from our code that the original versions of all these methods are also called because we are calling them ourselves with the super.on… code, which is the first thing we do in each of our overridden methods.

The quirk in our apps' behavior came when we used the task manager to switch between apps, and also, when switching away from Lifecycle Demonstration, it was not destroyed; subsequently, when switching back, it was not necessary to run onCreate.

Note

Where's my Toast?

Actually, the opening three and closing three Toast messages are queued and the methods have already completed by the time they are shown. You can verify this by running the experiments again and seeing that all three starting/closing log messages are output before even the second Toast message is shown. However, the Toast messages do reinforce our knowledge about the order, if not the timing.

It is entirely possible (but not that likely) that you got slightly different results when you followed the previous steps. What is for sure is that when our apps are run on thousands of different devices by millions of different users who have different preferences for interacting with their devices, Android will be calling the lifecycle methods at times we cannot really predict.

For example, what happens when the user exits the app by pressing the home button? When we open two apps, one after the other, and then use the back button to switch to the previous app: will that destroy or just stop the app? What happens when the user has a dozen apps in his task manager and the operating system needs to destroy some apps that were previously only stopped: will our app be one of the "victims"?

You can of course test out all of these scenarios on the emulator, but the results will only be true for the one time you test it. It is not guaranteed that the same behavior will be exhibited every time, and certainly not on every different Android device.

At last some good news! The solution to all this complexity is to follow a few simple rules:

  • Set up your app ready to run in the onCreate method
  • Load your user's data in the onResume method
  • Save your user's data in the onPause method
  • Tidy up your app and make it a good Android citizen in the onDestroy method
  • Watch out throughout the book for a couple of occasions when we might like to use onStart and onStop

If we do these things, and we will see how to over the course of the book, we can just stop worrying about all this lifecycle stuff and let Android handle it!

There are a few more methods we can override as well. So let's take a look at them.

Some other overridden methods

Almost certainly you have noticed that there are two other auto-generated methods in the code of all our projects so far. They are onCreateOptionsMenu and onOptionsItemSelected. Most Android apps have a pop-up menu, so Android Studio generates one by default; including the basic code to make it work.

You can see the XML that describes the menu in menu/menu_main.xml from the project explorer. The key line of XML code is this:

<item android:id="@+id/action_settings" android:title="@string/action_settings" android:orderInCategory="100" app:showAsAction="never" />

This describes a menu item with the text, Settings. If you run any of the apps we have created so far you can see the button, as shown next:

If you tap the button you can see it in action, as shown next:

So, how do the onCreateOptionsMenu and onOptionsItemSelected methods produce these results?

The onCreateOptionsMenu method loads the menu from the menu_main.xml file with this line of code:

getMenuInflater().inflate(R.menu.menu_main, menu);

It is called by the default version of the onCreate method, which is why we don't see it happen.

Note

Some very old versions of Android do this slightly differently but we will not need to concern ourselves with this in the context of this book.

The onOptionsItemSelected method is called when the user taps the menu button. This method handles what will happen when a particular item is selected. At the moment nothing happens, it just returns true.

Feel free to add Toast and Log messages to these methods to test out the order and timing I have just described.

We will be using menus in our first major app project. I just thought it a good time to quickly introduce these two methods because they have been lurking around in our code without an introduction and I didn't want them to feel left out.

Now that we have seen how the Android lifecycle works and have been introduced to a whole bunch of methods we can override to interact with the lifecycle, we had better learn the fundamentals of Java so that we can write some code to go in these methods, as well as our own methods.