Chapter 2. Exploring the Application Basics

I’m sure you’re ready to roll up your sleeves and write more code. However, there are a few topics in the realm of theory and design to cover in detail first. In this chapter, we’ll cover the basics of some essential building blocks, including the files, parts, and terms that make up a simple Android application; the Activity class, which controls a single screen; the Intent class, Android’s powerful communications class; and the Application singleton class, which can be accessed from all your components. There is one more key component for your Android toolset, the Fragment, but we’ll cover that later on; it’s not necessary to understanding the basics.

I recommend you open your IDE and follow along as I cover the working parts of an Android application. I’ll be using the Android project that you created in Chapter 1.

The Files

Any mobile application, in its most basic form, consists of a single screen that is launched by clicking an icon on the device’s main screen.

When the SDK creates a basic Android project, it also creates several files and important directories.

As with any project, before you start building the structure it’s important to at least take a quick look over the blueprints. In an Android project, it is the files and folders that make up that structure.

In Eclipse, you will find your source folders and Android manifest right in the root directory:

Image AndroidManifest.xml

Image /res

Image /src

In Android Studio, you will find your source folders and Android manifest under

Image /projectName/src/main/AndroidManifest.xml

Image /projectName/src/main/java

Image /projectName/src/main/res

Your application can have only one AndroidManifest.xml file, so I’ll refer to this file and concept simply as the manifest.

The Manifest

The AndroidManifest.xml file is your portal to the rest of the phone. In it, you’ll describe which of your components should receive what events. You’ll also declare, in the manifest file, what hardware and software your app will need permission to access. First, let’s take a look at the <manifest> declaration in the AndroidManifest.xml file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.peachPit"
   android:versionCode="1"
   android:versionName="1.0">

There are a few noteworthy items in this code. The package definition tells Android which Java package to look in for the class files that make up the components of your application. The next two variables are not particularly important right now, but they will become vital once you’re ready to ship your application to the Google Play Store. The versionCode is the number that helps Google Play alert users that an update is available. The versionName is a string that the application menus and Google Play display to the user as the current version of your app.

The Activity Class

Open your manifest file, located in <ProjectRoot>/AndroidManifest.xml (If you don’t open it in the IDE, make sure you open it with a text editor.) In a typical Android application, activities are the backbone of the operation. Essentially, their purpose is to control what is displayed on the screen. They bridge the gap between the data you wish to display and the UI layout files and classes that do the work of displaying the data. If you’re familiar with the popular Model-View-Controller (MVC) architecture, the activity would be the control for a screen. Here’s what the activity declaration looks like in the manifest file:

<activity android:name=".MainActivity"
   android:label="@string/app_name">
<!-- More on how the intent-filter works in the next section-->
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

The android:name tag tells the system what to place on the end of your package (from the manifest declaration) to find your class definition. For example, in my sample project at com.peachpit.MainActivity, the class loader looks for a class that extends the Activity class.

In order to be found, the file must reside under the src/com/peachPit directory. This is standard operating procedure for the language that Android uses.

Watching the Activity in Action

The activity, if used correctly, is an object that specifically controls a single screen.

Let’s talk about this mythical activity in terms of a real-world RSS news feed reader—a case study that can quickly explain what pages of theory would often miss. A developer typically uses one activity to list all feeds to which a user has subscribed. When a user taps a feed, the developer uses a second activity to display a list of available articles for that particular news feed. Lastly, when a user clicks a particular story, the developer uses a third activity to display article details.

It’s easy to see how activities fill a certain role (subscription list, article list, article detail). At the same time, the activities are general, in that the article list should be able to display a list of articles from any RSS feed, and the article-details activity should show the details from any article found through an RSS reader.

The most basic of activities

In its simplest form, an activity is an object that extends the Activity class. It only needs to implement the onCreate method and call setContentView to get a visible interface. Here’s what your activity looks like by default when you create a new project:

public class MainActivity extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
   }
}

In this code, the device calls the onCreate method as the activity is starting. onCreate tells the UI system that the setContentView method specifies the main layout file for this activity. Each activity can have one and only one content view, so once you set it, it can’t be changed. This is how the Android SDK forces you to use a new activity for each screen, because each time you want to change your root content view, you’ll need a different activity.

Implementing Your Own Activity

In most cases, the best way to understand something is to use it. With that in mind, let’s add a new activity to the project you created in Chapter 1. This will explain how the activity works, its lifecycle, and what you need to know while working with it. Here are the general steps you’ll need to follow.

1. Add an entry for the new activity to your manifest.

2. Create a new class that extends the Activity class.

3. Create a new file containing the XML layout instructions for this new activity, and add a new string literal for the layout to display (don’t worry, this sounds a lot harder than it actually is).

4. When all the files are in place, you’ll need to launch this new activity from your existing one.

Telling Android about your new activity

We need to add the activity declaration to the manifest so that the Android system knows where to find your new activity when it comes time to load and launch it.

1. Open the AndroidManifest.xml file in your IDE.

2. Add the following line inside the <application> tag and directly after the </activity> closing tag of the previous declaration:

Click here to view code image

<activity android:name=".NewActivity"/>

This little line tells the system where to find the new activity in your application package. In the case of my demo, the class loader knows to look for the activity at com.peachPit.NewActivity.

Next, you’ll need to put a file there for it to find.

Creating the NewActivity class

There are several ways to create a new activity, but here is the easiest way to do it in your IDE.

1. Right-click (or Control-click) the package name you’ve chosen (mine is com.peachPit).

2. Select New, then select Class.

3. Name your new class NewActivity.

4. In the Superclass field, click the browse button.

5. In the Superclass window search field, start typing Activity until you see Activity–android.app populate. When you see it, select it, and click OK.

Although a name is technically all you need to create a new class, adding the superclass from this window will ensure that you are always extending the class you intended. This can really be a lifesaver when you start dealing with compatibility components that have the same name across different libraries.

6. Make the following highlighted changes to your code:

Click here to view code image

package com.peachPit;
import android.app.Activity;
import android.os.Bundle;
public class NewActivity extends Activity{

   @Override public void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
   }
}

Notice that this code looks very similar to what is already in your existing activity. Let’s make it a little bit different. If the imports don’t automatically appear for you, choose Source > Organize Imports. This will make sure you have all the imports you need included, as well as remove any unused ones.

7. In the res/values/strings.xml file, add the following highlighted lines in the <resources> tag under the existing strings:

Click here to view code image

<resources>
   <!—other strings are omitted here for brevity-->
   <string name="new_activity_text">
       Welcome to the New Activity!
   </string>
</resources>


Image Note

"new_activity_text" is just a variable name. You can name it whatever makes the most sense for you and your project.


In these lines, you told Android that you want a new string with the name new_activity_text that can be accessed through Android’s resource manager. You’ll learn much more about the contents of the /values folder in later chapters. Next, you need to create a layout file for your new activity.

Creating a new screen layout

Here’s how you create a new layout.

1. Create a file named activity_new.xml inside the res/layout/ folder. It should sit next to the existing main.xml file (which is used by your existing activity). This activity_new.xml file should look almost exactly like main.xml, except you’ll need to add a reference to the string you just created.

2. Inside activity_new.xml, insert the following code to create a basic layout with a text view in it. By adding android:id="@+id/new_activity_text_view", you are assigning this TextView an ID so your Java code can reference it (you’ll learn more about TextViews later; for now you should know that they’re Android views that show text on the screen):

Click here to view code image

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >
<TextView
     android:id="@+id/new_activity_text_view"
     android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text="@string/new_activity_text" />

</LinearLayout>

I devote Chapter 3 to resource management and user interface design, but for now just keep in mind that the @ prefix is how you tell Android that you want to reference an ID, string, or drawable that is defined elsewhere as a resource.

Now that you have a new layout with a shiny new string, you’ll need to tell the NewActivity class that you want it to use this particular layout file.

3. Add the following highlighted line to the onCreate method of your NewActivity class:

Click here to view code image

public void onCreate(Bundle icicle){
   super.onCreate(icicle);
   setContentView(R.layout.activity_new);
}

setContentView is the method in which you tell Android which XML file to display for your new activity. Now that you’ve created a new class, string, and layout file, it’s time to launch the activity and display your new view on the screen.

Pushing the button

The simplest way to launch your new activity on a device is to touch it. Launching an activity just by touching the screen probably isn’t a very common use case in practice, but it makes for a good and simple example. Most new activities are started when a user selects a list item, presses a button, or takes another action with the screen.

Inside your MainActivity (the one that was auto-generated for you when you made the project), add the following code :

public class MainActivity extends Activity {
   public void onCreate(Bundle icicle){
      super.onCreate(icicle);
      setContentView(R.layout.activity_new);

      View view = getWindow().getDecorView()
         .findViewById(android.R.id.content);
      view.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            // Launch new Activity here!
         }
      });
   }
}

Important: After you add this code, in Eclipse choose Source > Organize Imports; in Android Studio, choose Code > Optimize Imports. If your IDE is confused about which OnClickListener to import, select View.OnClickListener (not Dialog.OnClickListener). Organizing your imports is something that you will need to do on a fairly regular basis while building applications, so this one might be worth learning the shortcuts for. Check out the Source menu in Eclipse or the Code menu in Android studio; the keyboard shortcuts will be listed next to the name.

By setting an OnClickListener on the view, you are letting the system know that you want a callback if this view is clicked. Don’t worry about the getWindow() and getDecorView() functions for now; they are just a quick way of catching clicks on the entire screen. Later on I’ll show you how to set these click listeners on actual buttons and other views.

Launching the activity

Finally, it’s time to launch the new activity. This will start your brief foray into intents. Each new activity is started as the result of a new intent being dispatched into the system (which processes it and takes the appropriate action). To start the first activity, you’ll need a reference of your application context and the class object for your new activity. Let’s create the new intent first.

1. Place the following code into your onClick method:

Click here to view code image

Intent startIntent=new Intent(MainActivity.this,
  NewActivity.class);

You will probably need to organize your imports after adding the Intent. You’re passing the new intent a context (MainActivity.this is an easy way to reference the parent activity, which itself is a subclass of Context) and the class object of the activity that you would like to launch. This tells Android exactly where to look in the application package. There are many ways to create and interact with intents; this is, however, the simplest way to start up a new activity.

2. Once the intent is properly constructed, it’s simply a matter of telling the Android system that you’d like to start the new activity. Put the following line into your onClick method after the intent:

startActivity(startIntent);

Your OnClickListener should now look like the following:

Click here to view code image

View view = getWindow().getDecorView()
   .findViewById(android.R.id.content);
view.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
      Intent startIntent = new Intent(MainActivity.this, NewActivity.class)
      startActivity(startIntent);
   }
});


Image Note

Throughout this whole process, the original activity has never once had access to the instance of the new activity. Any information that might pass between these two activities must go through the intermediary intent. You’ll learn how this is done in the section “The Intent Class.”


Trying it out

If you’re running your IDE and you’ve been coding along with me, it should now be a simple matter of spinning up the emulator and installing your new activity (the steps for which you should remember from Chapter 1). Once your new application has launched, press the center key to see the results of your labor (Figure 2.1).

Image

Figure 2.1 Here is your new activity!

Now that you know how to create and launch a new activity, it’s time to discuss how that process works. You’ll need to understand, for the purposes of UI layout and data management/retention later, what methods are called each time one of your activities makes its way onto, and off of, the screen.

The Life and Times of an Activity

Each activity lives a very short but exciting life. It begins when an intent that your activity is registered to receive is broadcast to the system. The system calls your activity’s constructor (while also starting your application as necessary) before invoking the following methods on the activity, in this order:

Image onCreate

Image onStart

Image onResume

When you implement an activity, it’s your job to extend the methods that make up this lifecycle. The only one you are required to extend is onCreate. The others, if you declare them, will be called in the lifecycle order.

Your activity is the top visible application, can draw to the screen, will receive key events, and is generally the life of the party. When the user presses the Back key from the activity, these corresponding methods are called in the following order:

Image onPause

Image onStop

Image onDestroy

After these methods have executed, your activity is closed down and should be ready for garbage collection.

In order to make sense of the activity’s flow, let’s quickly look over each lifecycle method in detail. Remember that you must evoke the superclass call in each of these methods (often before you do anything else) or Android will throw exceptions at you.


Image Note

onCreate is the only one of the application lifecycle methods that you must implement. I usually end up implementing only one or two of these methods, depending on what each activity is responsible for.


public void onCreate(Bundle savedInstanceState)

Android will call your declaration of the onCreate method as your activity is starting, and it will be called only once per activity lifecycle. Remember that if the user rotates the device, the activity will be destroyed and re-created, at which point onCreate would be called again, but that’s because it’s a new activity.

The onCreate method is the place to do things you want to do only once. For example, if the title for your activity is dynamic but will not change after the activity is started, onCreate would be where you’d want to reach into the view hierarchy and set up the title. This method is not the place to configure data that could change while the app is in the background or when another activity is launched on top of it—that we’re saving for onResume.

Further, if your app is running in the background and the system is running low on resources, your application may be killed. If that happens, the onCreate method will be called on a new instance of the same activity when your application returns to the foreground.

The onCreate method is also your one and only chance to call setContentView for your activity. This, as you saw earlier, is how you tell the system what layout you’d like to use for this screen. You call setContentView once you can begin setting data on the UI. This could be anything from setting the contents of lists to TextViews or ImageViews.

public void onStart( )

When starting up, your onStart method is called immediately after onCreate. If your app was put in the background (either by another application launching over yours or because the user pressed the Home button), onStart will be called as you resume but before the activity can interact with the screen. I tend to avoid overriding onStart unless there’s something specific I need to check when my application is about to begin using the screen.

public void onResume( )

onResume is the last method called in the activity lifecycle as your activity is allowed access to the screen. If UI elements changed while your activity was in the background, this method is the place to make sure the UI and phone state are in sync.

When your activity is starting up, this method is called after onCreate and onStart. When your activity is coming to the foreground again, regardless of what state it was in before, onResume will always be called. This is a great place to make network calls to make sure that data is refreshed every time you come back to this activity.

Hooray, your activity is now running!

After all this setup, configuration, and work, your activity is now visible to the user. Things are being clicked, data may be parsed and displayed, lists are scrolled, and things are happening! At some point, however, the party must end (perhaps because the user pressed the Back key), and you’ll need to wind things down.

onPause( )

onPause is the first method called by the system as your application is leaving the screen. If you have any processes or loops (animations, for example) that should be running only while your activity is onscreen, the onPause method is the perfect place to stop them. onPause will be called on your activity if you’ve launched another activity over the one you’re currently displaying.

Keep in mind that if the system needs resources, your process could be killed anytime after the onPause method is called. This isn’t a normal occurrence, but you need to be aware that it could happen.

The onPause method is important because it may be the only warning you get that your activity (or even your entire application) is going away. It is in this method that you should save any important information to disk, your database, or the preferences.

Once your activity has actually left the screen, you’ll receive the next call in the activity lifecycle.

onStop( )

When Android calls your onStop method, it indicates that your activity has officially left the screen. Further, onStop is called when the user is leaving your activity to interact with another one. This doesn’t necessarily mean that your activity is shutting down (although it could). You can only assume that the user has left your activity for another one. If you’re doing any ongoing processing from within your activity that should run only while it’s active, this method is your chance to be a good citizen and shut it down.

onDestroy( )

onDestroy is your last method call before oblivion. This is your last chance for your activity to clean up its affairs before it passes on to the great garbage collector in the sky.

Any background processes that your activity may have been running in the background (fetching/parsing data, for example) must be shut down on this method call.

However, just because onDestroy is called doesn’t mean that your activity will be obliterated; it just marks the application as ready to be cleaned up so that whenever the garbage collector requires more resources, it knows that it is allowed to garbage-collect this application. So if you have a thread running and downloading data in the background, it may continue to run and take up system resources even after the onDestroy method is called unless you explicitly cancel it here.

Bonus Round—Data Retention Methods

As mentioned, your process can be killed at any point after onPause if the system needs resources. The user, however, shouldn’t ever know that this culling has occurred. Android gives you two chances to save your state data for later use.

onSaveInstanceState(Bundle outState)

This method passes you a bundle object into which you can put any data that you’d need to restore your activity to its current state at a later time. You’ll do this by calling something like outState.putString(...) or outState.putBoolean(...). Each stored value requires a string key going in, and it requires the same string key to come back out. You are responsible for overriding your own onSaveInstanceState method. If you’ve declared it, the system will call it; otherwise, you’ve missed your chance.

When your previously killed activity is restored, the system will call your onCreate method again and hand back to you the bundle you built with onSaveInstanceState.

onSaveInstanceState will be called only if the system thinks you may have to restore your activity later—for instance, if you received a phone call. It wouldn’t be called if, for example, the user has pressed the Back key, as the device clearly has no need to resume this exact activity later. As such, this method is not the place for saving user data. Only stash temporary information that is important to the UI on this particular instance of the screen.

onRetainNonConfigurationInstance( )

When the user switches between portrait and landscape mode, your activity is destroyed and a new instance of it is created (going through the full shutdown-startup cycle of method calls). When your activity is destroyed and created specifically because of a configuration change (the device rotation being the most common), onRetainNonConfigurationInstance gives you a chance to return any object that can be reclaimed in your new activity instance by calling getLastNonConfigurationInstance. This is great for saving large amounts of data, like images or big network requests, that you wouldn’t normally be able to serialize into the outState from onSaveInstanceState.

This tactic helps make screen rotation transitions faster. Keep this in mind if it takes your activity a significant amount of time to acquire data that it plans to display on the screen. Instead, you can get the previously displayed data by using getLastNonConfigurationInstance.

Here’s an example of how to use onRetainNonConfigurationInstance:

@Override
public Object onRetainNonConfigurationInstance() {
   final DataModel data = getFetchedData();
   return data;
}
@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);

   DataModel data = (DataModel) getLastNonConfigurationInstance();
   if (data == null) {
      data = fetchData();
   }
}

You should now have a basic understanding of

Image Steps for creating a new activity

Image How an activity is started

Image The lifecycle of an activity

You have what you need to keep up as I go over more complex topics in later chapters. Fear not, I’ll come back to the activity in no time.

The Intent Class

Intents, in the Android platform, make up the major communication protocol for moving information between application components. In a well-designed Android application, components (activity, content provider, or service) should never directly access an instance of any other component. As such, intents are how these pieces are able to communicate.

A good half of this book could be dedicated to the creation, use, and details of the Intent class. For the sake of brevity and getting you up and running as fast as possible, I’ll cover only a few basics in this chapter. Look for intents throughout the rest of this book. They’re probably the most-used class in Android as a whole.

There are two main ways to tell the Android system that you’d like to receive intents sent out by the system, by other applications, or even by your own app:

Image Registering an <intent-filter> in the AndroidManifest.xml file

Image Registering an IntentFilter object at runtime with the system

In each case, you need to tell the Android system what events you want to listen for.

There are numerous ways of sending intents as well. You can broadcast them out to the system as a whole, or you can target them to a specific activity or service. However, to start a service or activity, it must be registered in the manifest (you saw an example of this in the previous demonstration on starting a new activity).

Let’s take a look at how to use intents in practice.

Manifest Registration

Why not register everything at runtime? If an intent is declared as part of your manifest, the system will start your component so that it will receive it. Registration at runtime presupposes that you are already running. For this reason, anytime you want your application to awaken and take action based on an event, declare it in your manifest. If it’s something your application should receive only while it’s running, register an IntentFilter (it’s an intent-filter when declared in XML, but an IntentFilter in your Java code) once your particular component has started.

Let’s go back to the initial application and look again at the activity’s entry in the manifest:

<activity android:name=".MainActivity"
   android:label="@string/app_name">
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

The android.intent.action.MAIN declaration tells the system that this activity is the main activity for your application. No parameters are needed to start it. It’s a good idea to list only one activity as MAIN in the manifest. This is also how ADB (the Android Debug Bridge) knows which activity to start up.

The android.intent.category.LAUNCHER category tells the system that the enclosing activity should be launched when your icon is clicked on the phone’s application dock. Further, it tells Android that you’d like the icon to appear in the app launcher drawer. This is an example of an intent-filter that’s created for you by Android’s project creation tools. Let’s add one of our own.

Adding an Intent

If you skipped the previous section about the Activity class, now may be a good time to go back and at least skim over the code. In that section, I showed you how to declare and launch a simple new activity. What I didn’t show you, however, was how to make that activity accessible to the system as a whole by declaring an <intent-filter> for it within your manifest. Let’s do that now.

1. Add an intent-filter to the NewActivity declaration:

Click here to view code image

<activity android:name=".NewActivity">
  <intent-filter>
     <action android:name="com.peachpit.PURPLE_PONY_POWER"/>
     <category android:name="android.intent.category.DEFAULT"/>
  </intent-filter>
</activity>

In this code, you’ve registered for intents containing the com.peachpit.PURPLE_PONY_POWER action and set the intent-filter category to default.

Now, lest you think I’m a crazed children’s toy enthusiast, I’ve used this rather absurdist action string to demonstrate a point—namely, that the only requirement for the action string is that it be unique for your particular component.

In the previous section, I showed you how to launch the new activity by using the following lines:

Click here to view code image

Intent startIntent = new Intent(MainActivity.this, NewActivity.class);
startActivity(startIntent);

This works, but it has one major limitation: It cannot be launched outside your own application’s context. This renders useless one of the most powerful features that the activity-intent model has to offer. Namely, any application on the device, with the right intent, can use components within your application.

Now that you’ve added the <intent-filter> to the sample project manifest, you can launch this particular activity anywhere with the following code:

Click here to view code image

Intent actionStartIntent = new Intent("com.peachpit.PURPLE_PONY_POWER");
startActivity(actionStartIntent);

Notice a very important difference between this code and the listing above it. When you create the intent in this example, you’re not required to pass in a Context object (the bundle of information that is required to communicate with the system at large). This allows any application, with knowledge of the required intents, to start the NewActivity class.

2. In your OnClickListener, replace your previous intent code with this new action intent code that’s highlighted below. It should look like this:

Click here to view code image

view.setOnClickListener(new OnClickListener() {
@Override
  public void onClick(View v) {
     Intent actionStartIntent = new Intent("com.peachpit.
PURPLE_PONY_POWER");

     startActivity(actionStartIntent);
  }
});

Now when you press the down key in the sample application, you’ll see the same activity launching using this new manifest-declared intent-filter.

If you’ve misspelled the intent’s action string or neglected to add the default category to your intent-filter, you may get an android.content.Activity NotFoundException.

This exception will be thrown by the startActivity method anytime you create an intent that the system cannot connect to an activity listed in a manifest on the device.

Registering for intent filters is not only the purview of the activity. Any Android application component can register to be started when an intent action is broadcast by the system.

Listening for Intents at Runtime

Another method for receiving events that pertain only to your application or for receiving events broadcast by the Android system itself is to listen for the intents at runtime.

Let’s say that your activity would like to show a special screen or take a custom action when the user enables Airplane mode. To do this, you’ll need to create a temporary IntentFilter and an inner BroadcastReceiver object instance.

Create a receiver

Let’s add the runtime BroadcastReceiver to the MainActivity class. A BroadcastReceiver is, as you can probably guess, an object with a single onReceive method. Remember to organize your imports after adding the BroadcastReceiver to make sure the class is recognized.

Change the MainActivity class to look like the following:

public class MainActivity extends Activity {
  private BroadcastReceiver simpleReceiver=new BroadcastReceiver() {
     public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals(
          Intent.ACTION_AIRPLANE_MODE_CHANGED)){
             Toast.makeText(context,
             R.string.airplane_change,
             Toast.LENGTH_LONG).show();
        }
     }
 };
//Rest of the Activity is here.
}

In this code, you are creating a locally accessible receiver for use within the activity. When the system calls onReceive, you’ll need to check what the intent’s action is. This is a good idea, as BroadcastReceiver could register for any number of different intents.

When you receive the event that you’re looking for, you’ll use Android’s Toast API to print a simple message on the screen (in this case, the contents of the string named airplane_change). In practice, this might be the time to show a screen indicating that network connectivity is required for the application to run correctly.

Tell Android what you want to hear

Now that you’ve created a BroadcastReceiver, you can register it with the system:

public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   IntentFilter intentFilter = new IntentFilter();
   intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
   registerReceiver(simpleReceiver, intentFilter);
}

This is going to be the primary way that your applications listen for information on the status of the system. Everything from the status of the battery to the status of the Wi-Fi radio is at your fingertips with this tool. You can find out more about what activities you can monitor, with what permissions, by looking in the Android SDK documentation for the Intent class.

Remember to stop listening

For every runtime registration that you create, you must also unregister it. If you would like to receive the events only when your activity is visible, onPause is the best place to turn off the receiver. If you’d like to listen for as long as your activity is running, even if it’s not visible, you’ll want to unregister in onDestroy. Wherever you decide to stop listening, simply call unregisterReceiver (a method implemented by your superclass) and pass in the BroadcastReceiver you created earlier, like this:

@Override
public void onDestroy(){
   super.onDestroy();
   unregisterReceiver(simpleReceiver);
}

Wrapping up the flight mode receiver

Again, this is all in the original MainActivity class made by the SDK when you created the project.

1. In the onCreate method, create an intent filter and add the action Intent.ACTION_AIRPLANE_MODE_CHANGED to it.

2. Add as many actions to this intent filter as you wish. When your receiver is called, you’ll have to sort out which intent actually triggered the onReceive method for the BroadcastReceiver by calling getAction() on the intent.

3. Test the code by holding the power button down; this will pop up a dialog with several options.

4. Enable Airplane mode. If you’ve done everything right so far, you should see a small message pop up along the bottom of the screen with your alert message in it.

5. Clean up by unregistering the BroadcastReceiver in onDestroy.

Handling colliding activities

You may be thinking to yourself “Self, what happens when more than one activity is registered for the same intent?” This is a very interesting question, one that Android resolves simply by asking the user.

If two activities listen for the same intent in their manifests, and an application attempts to start an activity with that intent, the system will pop up a menu giving users a list of possible applications to choose from (Figure 2.2).

Image

Figure 2.2 Do you want to open that YouTube link in the browser or in the app?

You’ve probably seen similar behavior hundreds of times on your desktop computer, such as when opening a file and being given a choice about which application you’d like to open it with.

This notion of many activities registering for the same intent can have delightful side effects. In Android, any application can register to share media with a given MIME time by using the android.intent.action.SEND action.

Figure 2.3 is what the Share tab on my phone looks like when I press it in the image gallery.

Image

Figure 2.3 Applications capable of handling the intent that you’re sharing

It is this ability to register for similar intents that allows seamless interaction, as each application registering this intent is given an entry on the Share menu. Clicking an entry in this list will start the registered activity and pass along as an extra the location at which the image can be accessed. What is an extra? Good question.

Moving Your Own Data

One of the major features of the intent is the ability to package and send data along with it. One activity should never directly manipulate the memory of another. However, they still must have a way to communicate information. This communication is accomplished with the help of the intent’s Extra bundle. The bundle can hold any number of string-primitive pairs. Perhaps the best way to illustrate this concept is with some code and an example.

Earlier, I showed you how to start a new activity by using an action-based broadcast intent.

Add the following highlighted code to the OnClickListener that you’ve dealt with before:

view.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
      Intent actionStartIntent = new
         Intent("com.peachpit.PURPLE_PONY_POWER");
      actionStartIntent.putExtra("newBodyText",
         "You touched the screen!");
      startActivity(actionStartIntent);
   }
});

You’re adding a string payload to the intent before using it to start an activity. Whoever receives the intent will be able to pull this string out (assuming they know it’s there) and use it as they see fit. Now that you know how to attach the data, let’s take a look at an example of retrieving and using the string in NewActivity’s onCreate method:

public void onCreate(Bundle icicle){
   super.onCreate(icicle);
   setContentView(R.layout. activity_new);
   Intent currentIntent = getIntent();
   if(currentIntent.hasExtra("newBodyText")){
      String newText = currentIntent.getExtras().
         getString("newBodyText");

      TextView bodyView = (TextView)findViewById(
         R.id.new_activity_text_view);
      bodyView.setText(newText);
   }

In the highlighted code, I’m getting the intent that was responsible for starting my NewActivity by calling getIntent. Next, I’m checking if this intent actually contains the newBodyText extra. Keep in mind that the intent may not contain the extra. If you forget to check for this case, you’ll quickly find yourself inundated with NullPointerExceptions. If the extra is there, I’ll pull it out and set the string as the new text in my display. The last two lines obtain a reference to the screen’s text view and change the text to be the contents of the extra. Don’t worry about the mechanics of that particular operation right now; you’ll learn about this topic in depth later.

Reviewing intents

You’ve learned how to register for, create, and use the basic functionality of an intent. As you now know, they can be registered for in the manifest or at runtime. They can be sent by any application on the phone, and any number of application components can register for the same intent.

The goal in this section was to get you started on the care and feeding of Android intents. In future chapters and tasks, you’ll work with intents again in many different contexts.

The Application Class

Typically, an Android application is a collection of activities, broadcast receivers, services, and content providers. The Application class is the glue that binds all these disparate pieces into a singular, unified entity. Every time a content provider, activity, service, or intent receiver in your manifest is initialized, an Application class is also spun up and available to it.

The Default Application Declaration

Looking in the AndroidManifest.xml file, you’ll see a typical Application declaration that looks like the following:

<application android:icon="@drawable/icon"
   android:label="@string/app_name">
<!—Activities, Services, Broadcast Receivers, and Content Providers -->
</application>

Here you can see the <application> tag. This part of the manifest typically contains information relevant to your application at large. android:icon tells the system what icon to display in the main application list. android:label in this case refers to another entry in the strings.xml file you were editing earlier.

Customizing Your Own Application

Adding your own application is very similar to the steps you’ve already gone through to add a new activity.

1. Add a name field to the existing AndroidManifest.xml declaration.

2. Create a new class in your package that extends the Application class.

3. Profit!

Let’s go over steps 1 and 2 in depth. You’re on your own for step 3.

The name

When it comes to the manifest, android:name refers not to the name of the object being described, but to the location of the class in your Java package. The Application declaration is no exception. Here’s what the opening tag of the application should look like with the new declaration:

<application android:icon="@drawable/icon"
   android:label="@string/app_name"
   android:name= ".SampleApplication">

In this declaration, you tell the system what icon you want to represent your application on the Android application drawer.

Once again, the class loader will look for your Application class by appending the contents of android:name to the end of your package declaration within the <manifest> opening tag. Now you’ll need to actually create this class to keep the class loader from getting unhappy.

The Application class

Here’s what you’ll need, at a very basic level, to have an Application of your very own:

import android.app.Application;
public class SampleApplication extends Application{
   public void onCreate(){
      super.onCreate();
   }
}

The Application can be a very simple class. It’s hard to understand what the Application can do for you until you consider a few things:

Image Activities are very transient.

Image Activities have no access to each other’s memory, and they should communicate through intents.

Image Because activities are constantly being stopped and started for a variety of reasons, there’s no way for your activity to know if it’s being started for the very first time in the run of your application. The Application class’s onCreate method, on the other hand, is called only when your app is being initialized. As such, it can be a good place to take actions that should happen only when your application is first started.

If you need a temporary holding pen for data that may span many activities, a data member that’s part of the Application can be a convenient place to store it. You must be very careful about adding data to the Application. Any single component declared in your manifest, from the simplest BroadcastReceiver to the most complex activity, will, before it’s created by the system, first create your Application object. This means you must make the Application’s onCreate method run as fast as you possibly can.

Accessing the Application

All your broadcast receivers, services, activities, and content providers have a method named getApplication provided to them by the appropriate superclass. When invoked, getApplication will return a reference to your Application object if you specified one in the manifest. Getting access to it, now that you’ve declared and created the class, is as simple as calling getApplication and casting the returned object to an instance of your own reference. Here’s what it looks like:

SampleApplication myApplication = (SampleApplication) getApplication();

That’s all there is to it. You can add public data members or context-sensitive methods to your own version of the Application, and with one call all your components have access to the same object, like so:

public class SampleApplication extends Application{
   public String username;
   public void onCreate(){
      super.onCreate();
   }
}

To access your newly added variable, simply do the object cast listed earlier:

public void onCreate(Bundle bundle){
   SampleApplication myApplication =
      (SampleApplication) getApplication();
   myApplication.username = "sparks";
}

Be sure that any data you put in the Application is relevant everywhere, because the overhead for allocating and initializing the Application can become a drag on startup times.

Wrapping Up

Over the course of this chapter, I’ve exposed you to the fundamental building blocks of an Android application. I used examples to get you started on

Image The manifest

Image Creating and using your own activities

Image Sending, receiving, and taking advantage of intents

Image Creating your own Application object

It’s my hope that through the rest of the book, you’ll be able to use the building blocks you’ve learned in this chapter to understand how an Android application functions. From here on out, I’ll be focusing more on how to do tasks rather than on the theories that back them. On that note, let’s start making screens that include more than just a single text view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset