Lab Notes

August 5, 2018

Learn Yourself Some Android in 45 Minutes or Less

A guide that I had originally written for a new lab assistant for an Android project several years ago. Originally written September 25, 2015.

This document is meant to be a collection of general observations about Android that may help one to get familiar with the Android platform. It is not meant to be a definitive guide to Android programming, but rather a supplemental guide that can be used in conjunction with a more "official" document, such as any of the Android programming guides that have been released by Google.

This is also in no way a final document ... chances are I will be adding to it and amending it over time. But it contains what I believe to be some of the most important things I've observed about Android during the time that I've been working with it.

Activities


Activity -- The basic building block of an Android application.

An Android application -- fundamentally -- is made up of one or more activities. Only one activity can be in the foreground of the application at any given time, the rest of the Activities are in the background.

In general, activities are never really "closed," even when the entire application is put into the background. Google believes that their garbage collector is better at making decisions than you are, so because of that there is no official way to "close" an application or an activity -- in general they must be closed by the Android garbage collector.

Activities operate within a lifecycle. This lifecycle is a relatively simple state machine, as can be seen to the left. Most of the time, an Activity will either be in the Resumed or Paused states. An Activity will seldom be in any of the other states, or it will only be in certain states only one time, such as is the case with the Created or Destroyed states.

When an activity is in the foreground of the Android phone, then it is said to be in the Resumed state. During this state, the application's UI thread is running, and this Activity is the UI context that is executing on that UI thread. If the activity or the entire application falls into the background, then the Activity moves into the Paused state. During this time, no UI thread activity occurs.

Now let's bring some Java into this. An Activity is a class (android.app.Activity) and this class has override- able methods that correspond to Activity state changes. By overriding some specific state change method, then one can instruct the Activity to do certain things when it's state change ... it would be prudent perhaps to stop network activity or save user data to a file when the Activity moves from the Resumed state to the Paused state, so this would be accomplished by overriding protected void onPause().

A Reference Guide of Override-able State Methods of android.app.Activity

public class Activity extends ApplicationContext {

    protected void onCreate (Bundle savedInstanceState)

    protected void onStart ()

    protected void onResume ()

    protected void onRestart ()

    protected void onPause ()

    protected void onStop ()

    protected void onDestroy ()

}

IMPORTANT! -- Make sure to always call the superclass method when you override an Activity state method. Failure to do so will cause strange application behavior, and may potentially cause you to yell curse words.

protected void onResume () {

super.onResume();

< your code here ...>

}

For more information about activities: Android Documentation on Activities

Intents


Intent -- Android's primary inter-process communications mechanism.

You might eventually find that making an application that has exactly one Activity is very limiting. Eventually, you are going to want to start up a second Activity and tell it to do things. Intents are used n order to start or resume additional application Activities. Intents are the primary mechanism of which applications communicate, both internally to their own components, and externally to other applications and operating system components. The operating system also uses the Intent system to tell applications about system events that occur.

Application Intents

From the Android documentation: "[An intent's] most significant use is in the launching of activities, where it can be thought of as the glue between activities."

Creating an Intent that is designed to launch another Activity within your application is straightforward:

Intent myIntent = new Intent(getActivity(), NextActivity.class)

NOTE: "NextActivity" represents the name of the Activity class that you wish to invoke.

Start Activity? Or Start Activity for Result?

Once the Intent is created, the next thing that needs to be done is to use the Application Context (android.content.Context) in order to start the target Activity. There are two calls that accomplish this, depending on what you want to do.

The simplest call is to start an Activity, and the Activity will run, and the originating Activity does not expect the initialized Activity to return any information about what happened. The startActivity call is used to do this.

startActivity(myIntent)

That's great for a very limited number of use cases where you just want to start an Activity, and then you don't really care what the outcome is.

Say, however, your Activity is for example a network transfer dialog, and you wish to know whether or not the transfer succeeded or failed? What do you do then?

This is where the second call comes in: startActivityForResult. This allows us to call another activity with the expectation that the activity we call is going to return some kind of status information about the success of the operation that it performs. When the activity finishes, then a call will be made to a callback function in the original Activity that we must define: onActivityResult.

Now, this is the slightly confusing bit: an Activity may only have one override of onActivityResult. But what if we have multiple downstream Activities that we are going to call, and we need a result back from each one? How do we differentiate one from the other?

The solution to this is through the request code mechanism. If we have one main Activity, and we have several peripheral Activities that return a result we are going to invoke via an Intent at some point, then we differentiate them by assigning an integer value to each of them. Each time onActivityResult gets called, it's request code parameter will be filled with the integer value of the peripheral Activity whom this result has come from.

Defining Request Codes

So first we can define some integer that will correspond to the target Activity. It's scope is only within a single Activity, so this number can be any arbitrary number:

public static final int MY_INTENT_RQEUEST = 10

Then we can make our call to start the activity.

startActivityForResult(myIntent, MY_INTENT_REQUEST)

Now let's focus on the Activity that we are invoking. In order to return something back to our main Activity, then we need to use the setResult method in order to denote whether our work has passed or failed:

public class NextActivity extends Activity {

    ...

    protected void onResume() {

        ...

        < do work >

        ...

        if (work succeeded) {

            setResult(Activity.RESULT_OK);

        }

        else {

            setResult(Activity.RESULT_CANCELED);

        }

        finish();

    }

    ...

}

Now, go back to our main Activity. The last thing we need to do is implement our onActivityResult method, and we need to add some code that checks which peripheral Activity this came from, and whether the Activity succeeded or failed:

protected void onActivityResult (int requestCode, int ResultCode, Intent data) {

    if (requestCode == MY_INTENT_REQUEST) {

        if (resultCode == Activity.RESULT_OK) {

            Activity succeeded in doing it's work

        }

        else {

            Activity failed

        }

    }

}

"Send Something a Little Extra" with Extras

Sometimes a simple Pass/Fail is not enough. Sometimes your Activity might have to send you back something more, like maybe your peripheral Activity is a form where the user has to put in some information. Your main Activity needs the information entered into this form. How do you move arbitrary data from one Activity to the other?

Intents are able to not only send status integers back and forth, but they can send arbitrary key-value data, called Extras. Extras can be used on any Intent, regardless of which pair of Activities it is being moved in between.

Placing an Extra into an Intent involves a single call after the Intent has been initialized:

myIntent.putExtra(String, <mixed>)

It is good practice to use a final String to hard-code the names of your Extra keys into your application. This minimizes the chance of creating a bug by having mis-matched Extra keys buried in two separate places within your code, which is a huge headache. Something like putting the following line into your Activity class:

public static final String EXTRA_MY_EXTRA_STUFF = "MyExtraStuff"

Not all data types are supported by the Extra mechanism. For the most part, it must be a primitive type or a Serializable data type. Getting an Extra that has been set in an Intent entails calling one of the defined getter functions.

A Reference Guide of Intent Extra Getter Methods

In general, the format for getter methods is public <*>get<*>Extra (String name), where <*> equals one of the following things:

--  boolean or boolean[]

--  android.os.Bundle

--  byte or byte[]

--  char or char[]

--  CharSequence or CharSequence[] or java.util.ArrayList<CharSequence>

--  double or double[]

--  float or float[]

--  int or int[] or java.util.ArrayList<Integer>

--  long or long[]

--  android.os.Parcelable or android.os.Parcelable[] or java.util.ArrayList<android.os.Parcelable>

--  Serializable

--  short or short[]

--  String or String[] or java.util.ArrayList<String>

For more information about Intents: Android Documentation on Intents