Coding Your Application - Building and Publishing Your First Application - Android Application Development For Dummies, 3rd Edition (2015)

Android Application Development For Dummies, 3rd Edition (2015)

Part II. Building and Publishing Your First Application

Chapter 5. Coding Your Application

In This Chapter

arrow Seeing how activities work in Android

arrow Coding your own activity

arrow Using the Android framework classes

arrow Installing an application

arrow Using debugging tools

arrow Testing your app in the real world

You’re probably eager to start coding your application. In this chapter, you write the Java code, from soup to nuts. Before you can start banging out bits and bytes, though, you need a firm understanding of activities.

Understanding Activities and the Activity Lifecycle

An activity is a single, focused action that a user can take. You can think of an activity like a “page” in your app. For example, an activity might present a list of menu items that a user can choose from, or it might display photographs along with captions. An app may consist of only one activity or (more commonly) several. Though activities may work together to appear to be one cohesive application, they work independently from each other. Almost all activities interact with the user, so the Activity class creates for you the window in which you can place your user interface (UI).

An activity in Android is an important part of an application’s overall ­lifecycle, and the way the activities are launched and put together is a fundamental aspect of the Android application model. Every activity is implemented as a subclass of the Activity base class.

The Activity lifecycle is one of the most important differences between Android and other phone operating systems. It’s complicated, but it’s an important set of concepts to grasp before you dive into developing Android apps.

The Activity lifecycle

Two important methods that almost all activities implement are

· onCreate: Where the activity is initialized. Most importantly, it’s where you tell the activity which layout to use by using a layout resource ­identifier — considered the entry point of your activity.

· onPause: Where you deal with the user leaving your activity. Any changes made by the user should be committed at this point (if you need to save them).

Activities in the system are managed as an activity stack. When a new ­activity is created, it’s placed on top of the stack and becomes the running activity. The previous running activity always remains below it in the stack and returns to the foreground only when the new activity exits.

To be a successful Android programmer, you must understand the importance of how and why the activity works behind the scenes. This will make you a better Android programmer and help you debug strange problems later.

An activity essentially has four states, as described in Table 5-1.

Table 51 Essential States of an Activity

Activity State

Description

Active/running

The activity is in the foreground of the screen (at the top of the stack).

Paused

The activity has lost focus but is still visible. (A new, non‐full‐size or transparent activity has the focus on top of your activity.) Because a paused activity is completely alive, it can maintain state and member information and remains attached to the window manager in Android.

Stopped

If an activity becomes obscured by another activity, it is stopped. It retains all state and member information, but isn’t visible to the user. Therefore, the window is hidden and will often be killed by the Android system when memory is needed elsewhere.

Destroyed

When the activity is paused or stopped, the system can reclaim the memory by asking it to finish, or it can kill the process. When it displays the activity again to the user, it must be completely restarted and restored to its previous state.

Figure 5-1 shows the important paths of an activity — the activity lifecycle.

image

Figure 51: The activity lifecycle.

The rectangles represent callback methods you can implement to respond to events in the activity. The shaded ovals represent the major states of the activity.

The activity lifecycle is a large and complex topic, and the following sections cover only the basics. If you want to read more about activity lifecycles, check out the “Activity Lifecycle” article in the Android documentation athttp://d.android.com/reference/android/app/Activity.html.

Important lifecycle loops

You may be interested in monitoring these three loops in your activity:

· The entire lifetime takes place between the first call to onCreate() and the final call to onDestroy(). The activity performs all global setup in onCreate() and releases all remaining resources in onDestroy(). For example, if you create a thread to download a file from the Internet in the background, it may be initialized in the onCreate() method. That thread can be stopped in the onDestroy() method.

· The visible lifetime of the activity takes place between the onStart() and onStop() methods. During this time, the user can see the activity onscreen (though it may not be in the foreground interacting with the user, which can happen when the user is interacting with a dialog box). Between these two methods, you can maintain the resources that are needed to show and run your activity. For example, you can create an event handler to monitor the state of the phone. The phone state can change, and this event handler can inform the activity of the phone entering Airplane mode and react accordingly. You would set up the event handler in onStart() and tear down any resources you’re accessing in onStop(). The onStart() and onStop() methods can be called multiple times as the activity becomes visible or hidden to the user.

· The foreground lifetime of the activity begins at the call to onResume() and ends at the call to onPause(). During this time, the activity is in front of all other activities and is interacting with the user. An activity normally toggles between onResume() andonPause() multiple times, for example, when the device goes to sleep or when a new activity handles a particular event — therefore, the code in these methods must be fairly lightweight.

Viewing activity methods

The activity lifecycle boils down to these methods:

public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}

All methods can be overridden, and custom code can be placed in all of them. All activities implement onCreate() for initialization and may also implement onPause() for cleanup. You should always call the superclass (base class) when implementing these methods.

Following an activity’s path

The movement of an activity throughout its lifecycle looks like this:

· onCreate(): Called when the activity is first created. You initialize most of your activity’s class‐wide variables here. onStart() is always called next. Killable: No. Next: onStart().

· onRestart(): Called after your activity has been stopped before being started again. onStart() is always called next. Killable: No. Next: onStart().

· onStart(): Called when your activity is becoming visible to the user. Followed by onResume() if the activity is brought to the foreground or onStop() if it becomes hidden from the user. Killable: No. Next: ­onResume() or onStop().

· onResume(): Called when the activity will be available for interacting with the user. The activity is at the top of the activity stack at this point. Killable: No. Next: onPause().

· onPause(): Called when the system is about to resume a previous activity or if the user has navigated away to another portion of the system, such as by pressing the Home key. This stage is typically used to commit unsaved changes to data that needs to be persisted. If the activity is brought back to the foreground, onResume() is called; if the activity becomes invisible to the user, onStop() is called. Killable: Yes, but only on Honeycomb (3.0) or earlier. Next: onResume() or onStop().

· onStop(): Called when the activity is no longer visible to the user because another activity has resumed and is covering this one. This may happen because another activity has started or a previous activity has resumed and is now in the foreground of the activity stack. It’s followed by onRestart() if this activity is returning to interact with the user or by onDestroy() if this activity is going away. Killable: Yes. Next: ­onRestart() or onDestroy().

· onDestroy(): The final call you receive before your activity is destroyed. This method gets called either because the activity is finishing (such as someone calling finish() on it) or because the system is temporarily destroying the activity to reclaim space. You can distinguish between these two with the isFinishing() method, which helps identify whether the method is finishing or the system is killing it. The isFinishing() method is often used inside onPause() to determine whether the activity is pausing or being destroyed. Killable: Yes. Next: Nothing.

The killable indicator at the end of each activity method description notes the activities the Android system can kill at any time and without notice. You should therefore use the onPause() method to complete any cleanup to write persistent data (such as user edits to data) to your storage mechanism.

Recognizing configuration changes

A configuration change is a change that’s made to the screen orientation (for example, if the user moves the screen to the side and back or moves it from portrait to landscape mode or vice versa), the language, or an input device. A configuration change causes your activity to be destroyed while completing the normal activity lifecycle: onPause() followed by onStop() and then onDestroy(). After the onDestroy() method is called, the system creates a new instance of the activity to be created, which takes place because resources and layout files and other elements might need to change depending on the current system configuration. For example, an application may look completely different if the user is interacting with it in portrait mode, as compared to being displayed in landscape mode (on its side).

Creating Your First Activity

You may have already created the MainActivity class if you created a project using the New Android Project Wizard in Chapter 3. Open the MainActivity.java file in the Silent Mode Toggle module to enhance it in the following sections.

Starting with onCreate

The entry point into your application is the onCreate() method. The code for the MainActivity.java file already contains an implementation of the onCreate() method. It’s where you start writing code! For now, your code should look like this:

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

// Initialize our layout using the res/layout/activity_main.xml
// layout file that contains our views for this activity.
setContentView(R.layout.activity_main); }
}

You write the initialization code directly below the setContentView() method.

Be sure to always include this method call to your onCreate() method:

super.onCreate(savedInstanceState);

It’s required for the application to run. This line directs the base Activity class to perform setup work for the MainActivity class. If you omit this line of code, you receive a runtime exception.

Telling Android to display the user interface

By default, an activity has no idea what its user interface is. It can be a simple form that allows the user to type information to be saved. It can be a visual, camera‐based, augmented, virtual reality application (such as Layar in the Google Play Store). Or it can be a drawn‐on‐the‐fly user interface, such as in a 2D or 3D game. As a developer, it’s your job to tell the activity which layout the activity should load.

To show the user interface onscreen, you have to set the content view for the activity, by adding this line of code:

setContentView(R.layout.activity_main);

R.layout.activity_main refers to the activity_main.xml file that’s located in the src/main/res/layouts directory. It’s the layout you defined in Chapter 4.

Handling user input

The Silent Mode Toggle application has little user interaction. The only user interaction that your application will have is a single button that the user taps to toggle Silent mode.

To respond to this tap event, you need to register an event listener, which responds to an event in the Android system. Though you find various types of events in the Android system, two of the most commonly used are keyboard events and touch events (also known as clicks).

Keyboard events

A keyboard event occurs whenever a particular keyboard key is pressed. For example, if the user presses the Alt+E hot key in your application, you may want the view to toggle into Edit mode. Responding to keyboard events allows you to do this. If you need to override theonKeyDown method to use your own keyboard event, do it this way:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return super.onKeyDown(keyCode, event);
}

You won’t need to use onKeyDown for the examples in this book, but it’s useful to know about it.

Touch events

A touch event occurs whenever the user taps a widget on the screen. The Android platform recognizes each tap event as a click event. Examples of views that can respond to touch events include (but aren’t limited to)

· Button

· ImageButton

· EditText

· Spinner

· ListView Rows

· MenuItem

All views in the Android system can react to a tap; however, some widgets have their clickable property set to false by default. You can override this setting in your layout file or in code to allow a view to be clickable by setting the clickable attribute on the view or the setClickable() method in code.

Writing your first click listener

For your application to respond to the click event of the user toggling Silent mode, you respond to the click event that’s exposed by the button.

Add the method shown in Listing 51 to your MainActivity class. It ­demonstrates how to implement a click handler for contentView. The code consists of the entire onCreate() method with the new code. You can either fill in the button code (in bold) or overwrite your entire onCreate code.

Listing 51: The Initial Class File with a Default Button OnClickListener

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

// Find the view with the ID "content" in our layout file.
FrameLayout contentView =
(FrameLayout) findViewById(R.id.content);

// Create a click listener for the contentView that will toggle
// the phone’s ringer state, and then update the UI to reflect
// the new state.
contentView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO
}
});
}

This listing uses the findViewById() method, which is available to all activities in Android. This method allows you to find any view inside the activity’s layout and do some work with it.

Be sure to cast the result of findViewById() to the appropriate type. If the type in your layout file is different from what you’re casting it to (if you’re trying to cast an ImageView in the layout file to ImageButton, for example), you’ll crash your application.

Immediately following this line of code, you start setting up the event ­handler.

The event handling code is placed inline after you retrieve the contentView from the layout. Setting up the event handler is as simple as setting a new View.OnClickListener. This click listener contains an onClick() method that’s called after the user taps the button. It’s where you place the code to handle the Silent mode toggle.

What should the view do when it’s clicked? You’ll set that up shortly, but for now leave it empty.

Working with the Android Framework Classes

This section gets into the good stuff — the nitty‐gritty of Android development and its Android framework classes! Yes, activities and views are integral parts of the system, but they’re simply the “plumbing” that’s required in any modern operating system (in one capacity or another). The real fun is just about to start.

The following sections describe how to check the state of the phone ringer to determine whether it’s in Normal mode (ringing loud and proud) or Silent mode. At this point, you can begin to start toggling the phone’s ringer mode.

Getting good service

To access the Android ringer, you’ll need access to the AudioManager in Android, which is responsible for managing the ringer state, so you should initialize it in onCreate().

All important initialization needs to happen in onCreate().

You first need to create a field of type AudioManager by the name of audioManager. Type this name at the top of your class file, directly after the class declaration line, as shown in Listing 52.

Listing 52: Adding the Class‐Level AudioManager Variable

package com.dummies.silentmodetoggle;

import android.media.AudioManager; →3

. . .

public class MainActivity extends Activity {

AudioManager audioManager; →9

@Override
public void onCreate(Bundle savedInstanceState) {
// Always call super.onCreate() first.
super.onCreate(savedInstanceState);

// Get a reference to Android’s AudioManager so we can use
// it to toggle our ringer.
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); →18

. . .
}
}

This list briefly explains what the numbered lines denote:

3 The import statement that brings in the necessary package so that you can use AudioManager.

9 The AudioManager field. Because it’s a field, you can have access to it in other parts of the activity.

18 Initializes the audioManager field by getting the service from the getSystemService() method in the Activity superclass.

Whoa! What’s getSystemService()? By inheriting from the base Activity class, MainActivity receives all the benefits of being an activity, including access to the getSystemService() method call. This method returns the base Java Object class, so you have to cast it to the type of service you’re requesting.

This call returns all available system services that you might need to work with. All services that are returned can be found in the Context class in the Android documentation, at http://d.android.com/reference/android/content/Context.html. Popular system service types include

· AUDIO_SERVICE

· LOCATION_SERVICE

· ALARM_SERVICE

Toggling Silent mode with AudioManager

After you have an instance of AudioManager, you can start checking the state of the ringer and toggling the ringer. The code you need to add or modify is in bold in Listing 53.

Listing 53: Adding the Application Toggle to the App

package com.dummies.silentmodetoggle;

import android.app.Activity;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.dummies.silentmodetoggle.util.RingerHelper;

public class MainActivity extends Activity {

AudioManager audioManager;

/**
* This method is called to initialize the activity after the
* java constructor for this class has been called. This is
* typically where you would call setContentView to inflate your
* layout, and findViewById to initialize your views.
* @param savedInstanceState contains additional data about the
*saved state of the activity if it was previously shutdown
*and is now being re-created from saved state.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
// Always call super.onCreate() first.
super.onCreate(savedInstanceState);

// Get a reference to Android's AudioManager so we can use
// it to toggle our ringer.
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

// Initialize our layout using the res/layout/activity_main.xml
// layout file that contains our views for this activity.
setContentView(R.layout.activity_main);

// Find the view named "content" in our layout file.
FrameLayout contentView =
(FrameLayout) findViewById(R.id.content);

// Create a click listener for the contentView that will toggle
// the phone's ringer state, and then update the UI to reflect
// the new state.
contentView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {

// Toggle the ringer mode. If it’s currently normal,
// make it silent. If it’s currently silent,
// do the opposite.
RingerHelper.performToggle(audioManager);

// Update the UI to reflect the new state
updateUi();
}
});
}


/**
* Updates the UI image to show an image representing silent or
* normal, as appropriate
*/
private void updateUi() {
// Find the view named phone_icon in our layout. We know it’s
// an ImageView in the layout, so downcast it to an ImageView.
ImageView imageView = (ImageView) findViewById(R.id.phone_icon);

// Set phoneImage to the ID of image that represents ringer on
// or off. These are found in res/drawable-xxhdpi
int phoneImage = RingerHelper.isPhoneSilent(audioManager)
? R.drawable.ringer_off
: R.drawable.ringer_on;

// Set the imageView to the image in phoneImage
imageView.setImageResource(phoneImage);
}

/**
* Every time the activity is resumed, make sure to update the
* buttons to reflect the current state of the system (since the
* user may have changed the phone’s silent state while we were in
* the background).
*
* Visit http://d.android.com/reference/android/app/Activity.html
* for more information about the Android Activity lifecycle.
*/
@Override
protected void onResume() {
super.onResume();

// Update our UI in case anything has changed.
updateUi();
}
}

Now, create the file src/main/java/com/dummies/silentmodetoggle/util/RingerHelper.java and add the following to it:

package com.dummies.silentmodetoggle.util;

import android.media.AudioManager;

public class RingerHelper {
// private to prevent users from creating a RingerHelper object
private RingerHelper() {}

/**
* Toggles the phone's silent mode
*/
public static void performToggle(AudioManager audioManager) {
// If the phone is currently silent, then unsilence it. If
// it's currently normal, then silence it.
audioManager.setRingerMode(
isPhoneSilent(audioManager)
? AudioManager.RINGER_MODE_NORMAL
: AudioManager.RINGER_MODE_SILENT);
}

/**
* Returns whether the phone is currently in silent mode.
*/
public static boolean isPhoneSilent(AudioManager audioManager) {
return audioManager.getRingerMode()
== AudioManager.RINGER_MODE_SILENT;
}
}

RingerHelper is a simple Java class that has only static methods that help us deal with the AudioManager ringer. These methods are useful in MainActivity now, but they’ll also be useful in other classes later, so that’s why they’re in a separate class.

Installing Your Application

You’ve done it — you’ve written your first Android app. Okay, your second, but your first one that does anything useful. In the next sections, you will install your app on the emulator and put that baby into action!

Running your app in an emulator

It’s time to install this app on the emulator. Follow these steps:

1. In Android Studio, choose RunRun ‘Silent Mode Toggle’.

You see the Choose Device window, shown in Figure 5-2.

2. If your emulator is already running, select it now.

Otherwise, select Launch Emulator and choose your desired emulator.

Click the Use same device for future launches check box to avoid having to see the dialog box every time you launch your app.

3. Wait for the emulator to load and launch your app.

Your application starts and the emulator runs your program, as shown in Figure 5-3.

If your application doesn’t start, try Step 1 again and watch the Android view to see the logcat output from your app. Refer to Chapter 3 for how to use the Android view.

4. Click the Toggle Silent Mode button to see the image toggle, shown inFigure 5-4.

Notice the new icon on the notification bar — the Silent Notification icon.

5. Return to the Home screen by clicking the Home button on the ­emulator.

6. Open the application (it’s the center button at the bottom of screen).

You see the application launcher icon in the list of applications.

image

Figure 52: The Choose Device ­window.

image

Figure 53: The emulator running the ­application.

image

Figure 54: The app in Silent mode, with the Silent Notification icon.

After the emulator is running, it’s running on its own. The emulator has no dependencies on Android Studio. In fact, you can close Android Studio and still interact with the emulator.

Installing on a physical Android device

Installing an application on a device is no different from installing it on the emulator, except for having to make a few small adjustments to get it to work. If you’re on a Windows machine, refer to Chapter 2 for how to install the ­necessary drivers. The remaining steps are straightforward:

1. From the Home screen of your phone, access the Settings panel.

2. Choose About Phone.

Tap on Build number seven times to unlock the developer options. You should see a message that says “You are now a developer!” If only you’d known that being an Android developer was so easy, you wouldn’t have needed to buy this book!

3. Go back to Settings and choose Developer Options, then select the USB Debugging option, as shown inFigure 5-5.

This step allows you to debug your application on a device. (You can find more about debugging later in this chapter, in the “Using the Android Studio debugger” section.)

4. Connect your phone to the computer by using a USB cable.

The phone will ask you whether you want to allow USB debugging for this computer. Click the Always allow from this computer check box and click OK.

5. When the phone is detected on your system, run the application by choosing RunRun ‘Silent Mode Toggle’.

Your device and any emulators that are currently running will show up in the device chooser (refer to Figure 5-2).

6. Choose your phone from the list and click OK.

This step sends the application to your phone, and it launches it just as it would on the emulator. In a few seconds, the app should show up on your phone.

You’ve now deployed the application to your phone.

image

Figure 55: Enabling your device to ­perform USB ­debugging.

If you change the app and you need to test it again, you have to reinstall it on your phone. It’s a simple matter of plugging in your phone and choosing Run⇒Run ‘Silent Mode Toggle’.

Material Design

Your app runs, it works great, and does what it says it will do. But does it feel right? You may have noticed that every time you click on the toggle button in the app, there’s no visual acknowledgment of your click. Sure the image toggles, but is there more that you can do?

Android’s visual design language, called Material Design, is all about making your phone’s UI look like physical materials. Backgrounds should look like card stock paper; views set on top of the background should be elevated to cast a shadow onto the background; button clicks should cause ripples that expand out over the view like ripples on a pond. These are the little details that make your app a delight to use.

Visit http://www.google.com/design/spec/materialdesign/ for more information about Material Design and how to use it to build a visually appealing app.

Your UI is quite simple right now, so there’s no need to elevate one part of it over another. But what you do need is some sort of click animation.

Luckily, it’s simple to add one. Go back to your activity_main.xml layout file, and change your FrameLayout to add the following line:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackground">

The foreground attribute in the FrameLayout class allows you to overlay a drawable on top of whatever is inside the FrameLayout. By setting your foreground to ?android:attr/selectableItemBackground, you are placing the Android‐standardselectableItemBackground over your entire image. What does the selectableItemBackground do? It’s a usually transparent drawable that when clicked displays a ripple animation across its view.

The question mark (?) in an attribute value means that you are referencing a value in the currently applied theme. If you change your app’s theme or run your app on a phone with another version of Android, the app will look and behave slightly differently (as it should) because you are referencing values from the proper theme.

Go ahead and run your app again, and you will see the standard Android ripple animation when you click your FrameLayout.

You will learn how to use other aspects of Material Design, such as setting your view elevation, in Chapter 9.

UhOh! (Responding to Errors)

You write perfect code, right? Even if it’s perfect this time, though, the day will come when it isn’t. When coding doesn’t go as planned, you have to figure out the problem. To help developers facing application crashes, Android Studio provides valuable tools to help debug applications.

Using the Android view

Debugging is rarely fun. Thankfully, the Android Tool window provides the tools necessary to help you dig yourself out of a hole filled with bugs. One of the most commonly used features in the Android Tool window is the logcat viewer, which allows you to view the output of system log messages from your system, as shown in Figure 5-6.

image

Figure 56: A view of logcat in the Android view.

This system log reports everything from basic information messages (which include the state of the application and device) to warning and error information. Seeing only an “Application Not Responding” or a force‐close error message on the device doesn’t clarify what has happened. Opening the Android view and reviewing the entries in logcat can help identify, down to the line number, where the exception is occurring.

Logging messages to logcat

Displaying log messages in the Android view is as simple as adding one line of code to your app. Open the MainActivity.java file, and at the bottom of the method, add a log entry, as shown in bold in Listing 54.

Listing 54: The onCreate() Method

import android.util.Log;

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

. . .

Log.d("SilentModeApp", "This is a test"); →9
}

Line 9 demonstrates how to output a message into the system log. SilentModeApp is known as the TAG that you’re giving to this log entry; the second parameter to the log call is the message you want to output. The tag helps filter messages while looking at them in Android Studio.

Declare a TAG constant in your code and use it instead of repeatedly typing the TAG, as in this example:

private static final String TAG = "SilentModeApp";

Another common technique for dealing with tags is to use the class name:

private static final String TAG = MainActivity.class.getSimpleName();

Notice the d in Log.d in Listing 54, indicating that this is a debug message. Other options are

· e: error

· i: info

· w: warning

· wtf: What a terrible failure (Yes, it’s an option.)

· v: verbose

The various logging types exist for you to decide how various messages should be logged.

Viewing log messages in logcat

You can view log messages in the Android Studio Android view by choosing View⇒Tool Windows⇒Android.

Start the application by choosing Run⇒Run ‘Silent Mode Toggle’. When your application is running, open the Android view and look for your log messages. It should look somewhat similar to the one shown in Figure 5-6.

By default, the Android view automatically filters the output for you to some sensible defaults. If you would like to explore other filters, try click­ing the filter selector in the top right of the Android view and select No filter. You can also filter by log level, and you can search for specific log messages if you like.

Using the Android Studio debugger

Although the Android view might be one of your best allies, your number‐one weapon in the battle against the army of bugs is the Android Studio debugger, which lets you set various breakpoints, inspect variables using the watch window, and much more.

Checking runtime errors

The runtime error is the Wicked Witch of the East — it comes out of nowhere and leaves everything a mess. Your application might be humming along and, all of a sudden, it crashes when you click a menu option or a button, for example. It can be very difficult to solve these kinds of problems just by looking at the source code.

The debugger can help in this situation because you can set a breakpoint at the start of onCreate() that allows you to inspect the values of the variables as the app is running.

Let’s make your app crash! Listing 55 demonstrates one way to crash your app — commenting out the setContentView initialization will cause an exception to be thrown at runtime. Go ahead and do this now:

Listing 55: Commenting Out the setContentView Initialization

@Override
public void onCreate(Bundle savedInstanceState) {
// Always call super.onCreate() first.
super.onCreate(savedInstanceState);

// Get a reference to Android's AudioManager so we can use
// it to toggle our ringer.
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);

// Initialize our layout using the res/layout/activity_main.xml
// layout file that contains our views for this activity.
//setContentView(R.layout.activity_main); →12

// Find the view named "content" in our layout file.
FrameLayout contentView =
(FrameLayout) findViewById(R.id.content); →16

// Create a click listener for the contentView that will toggle
// the phone's ringer state, and then update the UI to reflect
// the new state.
contentView.setOnClickListener(new View.OnClickListener() { →21
public void onClick(View v) {
. . .
}
});
}

Listing 55 works this way:

12 This code, which is intentionally commented out, is a bug and ­prevents our activity from getting a layout.

16 findViewById won’t be able to find R.id.content anymore, so it will return null.

21 Calling contentView.setOnClickListener will throw a NullPointerException because contentView is null.

Attaching a debugger to your app allows you to track down the root cause of the error.

Creating breakpoints

You have a couple ways to create a breakpoint, which will pause your ­application mid‐execution and let you examine its running state:

· Choose the line where you want to place the breakpoint by clicking it with the mouse. Choose Run⇒Toggle Line Breakpoint.

· Click the left gutter in the Android Studio editor where you want to create a breakpoint.

Either method creates a small, round red icon in the left gutter of the Android Studio editor, as shown in Figure 5-7.

image

Figure 57: A set breakpoint in the left gutter of Android Studio’s ­editor ­window.

To try debugging in Android Studio, set a breakpoint on the line with ­contentView.setOnClickListener.

Starting the debugger

Follow these steps to debug your code:

1. Choose RunDebug ‘Silent Mode Toggle’.

Android Studio installs the application on the emulator (or device) and then attaches the debugger to it. If your emulator is not running, you will have the option to start one now.

2. Wait for the debugger to break at your breakpoint.

You’re now at a breakpoint, as shown in Figure 5-8. You can hover the cursor over variables to see their values.

3. Hover the cursor over the contentView variable.

The variable is null because you commented out the code. Silly human, why did you do that?

If you click the Resume button and look at your emulator, you can see that your application has now crashed, as shown in Figure 5-9.

4. To disconnect the debugger, click the Stop button.

image

Figure 58: The SilentMode‐Toggle app running in the Debug view.

image

Figure 59: The app crash dialog box opens after an exception.

Go back to MainActivity.java and remove the comment you added to ensure that the application runs successfully.

Thinking Beyond the Application Boundaries

At times, the device may perform extraneous work that can affect your application, such as downloading a large file in the background while playing music from an online radio application. Will these heavy network‐bound activities affect the application in any way? It depends. If your app needs a connection to the Internet and for some reason cannot connect, will it crash? What will happen? Knowing the answers to these questions means that you’re thinking beyond your application boundaries.

Not all apps are created equal — some good ones are out there, along with some bad ones. Before building or releasing your first Android application, ensure that you know the ins and outs of your application and anything that can affect it. Be sure that the app doesn’t crash when users perform routine tap events and screen navigation.

Building applications on embedded devices is very different than building them on a PC or Mac, and the reason is simple: The resources (battery, memory and processor, for example) are limited. If the Android device is a phone, its main purpose is to perform phone‐like duties, such as recognizing an incoming call, maintaining a signal, and sending and receiving text ­messages.

If a phone call is in progress, the Android system treats that process as vital, whereas a downloading file in the background is considered non‐vital. If the phone starts to run out of resources, Android kills all non‐vital processes to keep the vital ones alive. A file can be downloaded again, but when a call is lost, it’s lost forever — you have to make that call again, which would only frustrate the user if the main purpose for purchasing the device was to have a phone. Your app might download a file in the background and the process gets killed — this is a scenario that you need to test. It can also happen if your phone encounters an area with a poor or non‐existent wireless signal. If the connection gets dropped, your file isn’t downloaded.

Test for all possible solutions and have a safety guard for them. Otherwise, your app will be prone to runtime exceptions, which can lead to poor reviews from users at the Google Play Store.

Interacting with your application

To ensure that your app works, fire it up and play with its features. While your app is running, start another app, such as the browser. Visit a few sites, and then return to your app. Click any buttons related to your app to see what happens. Try all kinds of things to see whether you find outcomes that you didn’t consider. What happens if a user is interacting with your app and receives a phone call? Are you saving the necessary state in onPause() and restoring it in onResume()?

Android handles the difficult task management for you, but it’s ultimately your responsibility to manage the state of your application.

The most common errors come from Android developers failing to save their state properly in onPause and restore it in onResume. Remember that Android can kill your activity at any time, and it’s up to you to make sure you properly save your activity’s state so it can be re‐created later if necessary! See Chapter 10 for more information about saving and restoring your activity state.

Testing whether your application works

In the emulator or on your device, open the Silent Mode Toggle application from the launcher. You’ve already performed the first step in the testing ­process — making sure that the app starts!

After the app is open, check to see whether the phone is in Silent mode by looking for the small star icon on the notification bar (refer to Figure 5-3).

Click the Silent Mode Toggle button to toggle the ringer mode. Did the application’s image change? Try various actions to ensure that your application works as expected. If you find a flaw, use the debugging tools featured in this chapter to help identify the issue.

Are you having difficulty turning Silent mode off again? You may have been hit by a bug introduced in Android 5.0. Visit https://code.google.com/p/android/issues/detail?id=78652 for more details.

What about automated testing?

With the rise of agile methodologies over the past decade, it’s only a matter of time before you start to wonder how to perform automated testing in Android. The SDK installs Android unit‐testing tools that you can use to test not only Java classes but also Android‐based classes and user interface interactions. You can read more about unit testing in the Android documentation at http://d.android.com/guide/topics/testing/testing_android.html.

Here are some tools at your disposal:

· JUnit: The Android SDK includes JUnit 3.x integration. You can use JUnit, a popular unit‐testing framework that’s used in Java, to perform unit testing or interaction testing, and you can find more information about JUnit at www.junit.org. To make your development life easier, Android Studio has built‐in tools to help facilitate testing in JUnit through Android Studio.

· Monkey: The user interface and application exerciser known as Monkey runs on your emulator or device and generates pseudorandom streams of user events, including taps, gestures, touches, clicks, and a number of system events. Monkey, which is installed with the Android SDK, is a helpful way to stress‐test an application.

· UI Automator: The UI Automator testing framework lets you test your user interface (UI) efficiently by creating automated functional UI test cases that can be run against your app on one or more devices.

· Espresso: The Espresso library makes unit testing Android significantly easier than using straight JUnit. It uses a simple and concise style to write Android unit tests. Beginning with 2.0, Espresso is now distributed as part of the Android SDK.

To learn more about how to use Espresso to create automated tests for your app, visit the book’s online website at www.dummies.com/extras/androidappdevelopment to read the articles on testing.