Monday, September 15, 2014

Starting From Scratch: Android - Activities


This week we're continuing the Starting From Scratch series. Today we're going to take a look at Android Activities. I'll discuss what an Activity is, how you create one, the Activity life-cycle, launching an Activity programmatically, and finally I'll give a few Activity tips I've found along the way.

What Is An Activity


According to the Android documentation, an Activity is "... a single, focused thing that the user can do." Yeah, that's pretty vague. So let's break it down.

In the early days of Android (pre-tablet) an Activity represented pretty much everything on the screen, how it got there, and how you interact with it. Basically the Activity was analogous to a "screen" in an application. If you wanted to display a different screen you either had to dynamically tear down the current screen and rebuild the new screen (very laborious) or, preferably, start a new Activity which owned the UI and interactions for the new screen. This allowed developers to encapsulate the concepts within a screen pretty nicely and manage workflow easier.

As Android matured, and tablets became mainstream, it became necessary to reuse portions of an Activity within other Activities. Thus was born the Fragment (which I'll talk about later in this series). Prior to Fragments a typical application would have many Activities. Once Fragments were introduced the number of Activities an application had were reduced from one per screen to one per workflow.

Creating an Activity


There are two steps necessary when creating an Activity. The first is to write the Java class which extends the Activity class. This class should implement any necessary life-cycle events (which are outlined below). The second step is to declare the Activity in the Android manifest. You can see an example of this by looking at the initial Activity that was created for your application in your AndroidManifest.xml.

Activity Life-cycle


An Activity has several states that it can be in. Those states are associated with creating, starting, resuming, pausing, stopping, or destroying the Activity.

Creating


When an Activity if first being created a call will be made to the Activities onCreate method. The onCreate method is responsible for associating a UI layout with the Activity. This is done via a call to the setContentView method with the associated layout ID. As mentioned previously in this series, a layout is a composition of the UI elements to display on screen.

For Example:
@Override 
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

If you have any state that needs to be restored before the official call to onRestoreInstanceState you can do it after the call to setContentView using the saved instance state Bundle passed in. The typical reasons you may want state restored here is if you want to be able to make any decisions on the other life-cycle methods based on the current state of the Activity. Because of this, the onCreate method is passed a Bundle which will contain any state that was saved when the Activity was paused.

You cannot assume that the Bundle passed in to the onCreate method will exist. In the case that there is no previous state or if it's the first time the Activity is run the Bundle will be null. Therefore it is very important to check to make sure the Bundle exists before you try to use it. If the Bundle exists you can use the information you've saved in it to restore the internal state of the Activity.

NOTE: It is really important to draw the distinction here between resetting the internal state of an Activity and resetting the UI of an Activity. YOU SHOULD NOT try to interact with the UI in the onCreate method. The onCreate method is called when the Activity is starting but before it is displayed on the screen. If you try to get a handle to UI elements they will not exist.


Restarting


The onCreate method is only called when the Activity is not already in memory. Often it is the case that the Activity has been paused but not cleared out of memory. In this case when the Activity is about to be re-started the onRestart method is called. For most people there really isn't anything you need to do in onRestart. The internal state of your Activity will be the same as when it was paused, so there is nothing to really restore. The exception being if you are using a Cursor.

@Override 
public void onRestart() {
}

Starting


The onStart method is called after either onCreate or onRestart when the Activity is visible to the user. The Activity UI is not yet ready to be interacted with. At this point you still don't want to do anything programmatically with UI elements but you are able to reset internal Activity data.

@Override 
public void onStart() {
}

Resuming


Finally, we're at the point in the Activity life-cycle where you can do something with the UI. The onResume method is called right after the Activity has been displayed on the screen. At this point the UI is ready to be interacted with. If you did not register your UI event listeners in the layout file this is where you would want to register those listeners.
@Override 
public void onResume() {
}


At this point in the life-cycle your Activity is considered to be running.

Pausing


When your Activity is about to go into the background, but it is still active, the onPause method is called. This happens when another Activity is about to be put on top of your Activity (while it's still running) or your Activity is about to be put into the background or killed.
@Override 
public void onPause() {
}

This is a good time to commit changes to data and unwire any listeners that you've set up. It's important to note that onPause doesn't necessarily mean that your Activity is going into the background. This will also be called when a dialog is displayed on top of your Activity. In this case the next life-cycle event will be onResume instead of onStop.

It's important that this method returns very quickly as the onResume method for the Activity about to show will not get called until this method returns.

Stopping


The onStop method is called when another Activity has come to the foreground and fills the entire screen. On stop cause be used to save state as well as stop things that are time intensive.
@Override 
public void onStop() {
}

Destroying


The onDestroy method is called right before your Activity is about to be removed from memory. This is a good time to clean up the internal handles that your Activity may have.
@Override 
public void onDestroy() {
}

Saving State


In the previous life-cycle events I mentioned saving/restoring Activity state several times. Users will expect an Activity to be in the same state that they left it in. The user can leave the Activity for a variety of reasons like to answer a phone call or to use another app. Sometimes when a user has a lot of applications running or is doing something very memory intensive Android needs to completely remove the Activity from memory when it's not on the screen. In that case displaying the Activity again will cause it to re-display as if it was never on the screen to begin with. This is why it is important to make sure you restore the internal Activity state if there is any to restore

Saving state is done via the onSaveInstanceState method. Restoring state is done via the onRestoreInstanceState method. The onSaveInstanceState method is called before the Activity is killed. If onSaveInstanceState is called, it will be called before the onStop life-cycle event is called. The onSaveInstanceState is passed a Bundle which should be used to save instance state too. That bundle will then be passed back to the onCreate life-cycle event.

onRestoreInstanceState is called after onStart and is used to restore any state saved in the call to onSaveInstanceState. For example:
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString("SomeVariable", someVariable);
@Override
public void onRestoreInstanceState(Bundle savedState) {
    if(savedState != null) {
        this.someVariable = savedState.getString("SomeVariable");
    }
}

Launching an Activity


There are two main ways to launch an Activity. The first way is to specify the type of action you want performed and allowing the user to select the appropriate application they want to handle that. This is commonly used when sharing data or launching a url.
private void openUrlInBrowser(String url) {
    Uri uri = Uri.parse(url);
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    this.startActivity(intent);
}

The second way to launch an Activity is by class name. This is the case when you want to launch a very specific Activity. Typically this is how you would launch another Activity within your application.

private void openSomeOtherActivity() {
    Intent intent = new Intent(this, SomeOtherActivity.class);
    this.startActivity(intent);
}

In either case launching an Activity is a two step process. The first step is to create an Intent which details what you want to do. You decide how you want to launch the Activity based on the Intent constructor you select. The second step is to call the startActivity method from your Activity.


Activity Tips



  • Only restore an Activities internal state in onCreate. Never try to restore UI.
  • The first time an Activity is being displayed, the Bundle passed into onCreate will be null.
  • Reset an Activities UI in onResume.
  • Don't change the name of the Activity used to start the application. Changing that will cause the any home screen icons associated with the previous Activity to be broken after the application is updated. The is because the icons are associated with the previous Activity name.



No comments:

Post a Comment