Global Variables in Android Apps

Using the Application object to cure a case of the Android flip flops

Losing your religion

I love software — really I do.

I love to create stuff.

Some of my favorite words to hear are: "Would it be possible to…?"

The answer to this oft-spoken query is usually something between: "Why would you want to do that?" and "Of course, let’s see it in action."

I love the "Get it done" aspect to writing software. Creating proto-types to test out an idea is arguably my favorite thing to do professionally.

Production code is OK — if you can make a living writing it. It tends to get boring very quickly.

If the code can be written well, that is great too.

And if the code is easily maintained over time, that is even better. And usually, my code is. Except for the spurious comment that just says:

// punt

My development team often laughs at these lines and asks me just what I meant when I wrote it. If I remembered why I wrote it, I probably wouldn’t have a development team to back me up, but that is a story for another day!

I admit, I am probably a bit too loose with my programming style at times. Perhaps it is a character flaw but I have often had a difficult time when people take their programming languages (and themselves) too seriously.

One line I often hear is this: "I am a Java programmer". Great, welcome to the human race, Mr./Ms. Java Programmer.

I have a good friend who is always asking me if my code can be easily re-factored. Of course. Who doesn’t?

While I respect good programming practices, I shy away from people who “worship” one style of programming over another.

If you have been around programmers like these for any period of time, particularly “object oriented programmers”, you have likely heard some of these lines:

  • Never use globals. Never.
  • Your code will not be easy to maintain — break it into more classes.
  • Use hunGarian notation.
  • Encapsulate or perish.

Most of this advice is good — in reasonable doses. Some of it is just limiting the imagination — or the other evil — burdening us with excessive programming constructs. Heretical, I admit.

So, what does this have to do with Android you ask?

Simply that there are some things in Android which are quite happily solved through the use of the often maligned global variable — let’s have a look.

Activity Flip-Flopping

No, Android is not yet running for office, so we have little fear that the story will change over night.

We are concerned here with the funny behavior of our trusted friend Android when a device’s orientation is switched from Portrait to Landscape or vice versa.

In a prior article we learned how to take a photo with a simple Android application.

Taking a picture
Taking a picture

Unfortunately, if the phone is rotated, the Android Activity is re-created (onCreate() method is invoked) and our image is lost!

Losing the photo when rotating
Losing the photo when rotating

Placing a call to trace the execution of the onCreate method, we can actually see that the method is invoked again on each rotation.

05-10 23:15:23.601: INFO/com.msi.linuxmag.PhotoBooth2(7263): Activity Ready to Go!
05-10 23:15:24.481: INFO/ActivityManager(71): Displayed activity com.msi.linuxmag/.PhotoBooth2: 1180 ms (total 1180 ms)
05-10 23:15:27.361: DEBUG/ViewFlipper(134): updateRunning() mVisible=false, mStarted=true, mUserPresent=true, mRunning=false
05-10 23:15:30.401: INFO/WindowManager(71): Setting rotation to 1, animFlags=0
05-10 23:15:30.401: INFO/ActivityManager(71): Config changed: { scale=1.0 imsi=310/410 loc=en_US touch=3 keys=1/1/2 nav=3/1 orien=2 layout=34}
05-10 23:15:30.461: INFO/UsageStats(71): Unexpected resume of com.msi.linuxmag while already resumed in com.msi.linuxmag
05-10 23:15:30.641: INFO/com.msi.linuxmag.PhotoBooth2(7263): Activity Ready to Go!
05-10 23:15:34.171: INFO/WindowManager(71): Setting rotation to 0, animFlags=0
05-10 23:15:34.171: INFO/ActivityManager(71): Config changed: { scale=1.0 imsi=310/410 loc=en_US touch=3 keys=1/1/2 nav=3/1 orien=1 layout=34}
05-10 23:15:34.221: INFO/UsageStats(71): Unexpected resume of com.msi.linuxmag while already resumed in com.msi.linuxmag
05-10 23:15:34.291: INFO/com.msi.linuxmag.PhotoBooth2(7263): Activity Ready to Go!
05-10 23:15:37.301: INFO/WindowManager(71): Setting rotation to 1, animFlags=0
05-10 23:15:37.301: INFO/ActivityManager(71): Config changed: { scale=1.0 imsi=310/410 loc=en_US touch=3 keys=1/1/2 nav=3/1 orien=2 layout=34}

As a result of this "reload", any Activity-scoped variables are reset to their initial values — we need a solution to this obstacle.

In the log-listing above note the lines referring to "Config changed". This is at the root of what is happening — the configuration is changing.

Configuration here is somewhat broadly defined. It could refer to the keyboard layout changing — such as when a G1 or a Droid slides out its keyboard. Or a input language/locale changes. Or, in our case, the device is rotated.

An application can specifically declare within its AndroidManifest.xml which types of configuration changes it wants to handle. In absence of this declaration, the default behavior is for the currently running Activity to relaunch.

We will examine this declarative configuration management in more detail in an upcoming article. For now, we’re going to solve our problem with one of the oldest programming techniques around — using the global variable space.

Application object to the rescue

By default, the Android application that is created with the “File->New” Android project does not contain an implementation of the android.app.Application class. To make use of this class, we must first sub-class it, as shown below.

package com.msi.linuxmag;

import android.app.Application;
import android.graphics.Bitmap;
import android.util.Log;

public class PhotoBoothApp extends Application {
	public Bitmap b = null;
	public PhotoBoothApp ()
	{
		super();
		Log.i("PhotoBooth2","PhotoBoothApp constructor()");
	}
}

In our case, the class is really very simple — we extend the android.app.Application class, declare a “global” variable named b which will hold our Bitmap, and implement the constructor. The constructor is trivial as we simply invoke super() to ask the parent class to do its initialization and we drop a line to the log for a sanity check that this code is being called.

Now, for those who would rather go to the dentist than write code that contains a public member variable, you can wrap that in a “getter” if you prefer and make the Bitmap instance Private.

package com.msi.linuxmag;

import android.app.Application;
import android.graphics.Bitmap;
import android.util.Log;

public class PhotoBoothApp extends Application {
	private Bitmap b = null;
	public PhotoBoothApp ()
	{
		super();
		Log.i("PhotoBooth2","PhotoBoothApp constructor()");
	}
	public Bitmap getBitmap()
	{
		return b;
	}
	public void setBitmap(Bitmap newBitmap)
	{
		b = newBitmap;
	}
}

Regardless of how much encapsulation you prefer, the key here is that this code is accessible to all of the Activity instances in our application. Let’s have a look at how we access this newly minted class from our Activity. And just for fun, I’m going to use the version which marks the Bitmap instance as Public!

Within an Activity, we call the getApplication() method to return an instance of the one and only Application object implemented in our application. Sorry for the multiple uses of the word “application”. The capitalized form refers to the android.app.Application class.

Our Activity code has been updated to no longer rely on a Bitmap object defined at the Activity level — we’re now going to rely on our “global” Bitmap instance.

In the onCreate() method, we now look to see if we have a good reference to a Bitmap object. If so, we display it by assigning it to the ImageView widget defined in our screen layout.

       public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.i(PhotoBooth2.class.getName(),"Activity Ready to Go!");

        final Button  btn = (Button) this.findViewById(R.id.TakePhoto);
        btn.setOnClickListener(new View.OnClickListener(){

        	public void onClick(View v){
        		Log.i(PhotoBooth2.class.getName(),"Button Clicked!");
        		try {
        			Intent action = new Intent("android.media.action.IMAGE_CAPTURE");
        			startActivityForResult(action,1);
        		} catch (Exception e) {
        			Log.e(PhotoBooth2.class.getName(),"Error occured [" + e.getMessage() + "]");
        		}
        	}
        });

        iv = (ImageView) this.findViewById(R.id.PictureFrame);
        PhotoBoothApp app = (PhotoBoothApp) getApplication();
        if (app.b != null) {
        	iv.setImageBitmap(app.b);
        }
    }

After successfully taking a photo, we want to store the image into our PhotoBoothApp object’s Bitmap instance.


    protected void onActivityResult(int requestCode,int resultCode,Intent data)
    {
    	try {
    		if (requestCode == 1) {
    			Log.i(PhotoBooth2.class.getName(),"resultCode is [" + resultCode + "]");
    			if (resultCode == RESULT_OK) {
	    			PhotoBoothApp app = (PhotoBoothApp) getApplication();
    				if (app.b != null) app.b.recycle();
	    			app.b = (Bitmap) data.getExtras().get("data");
	    			if (app.b != null) {
	    				iv.setImageBitmap(app.b);
	    			}
    			}
    		}
    	}catch (Exception e) {
    		Log.e(PhotoBooth2.class.getName(),"onActivityResult Error [" + e.getMessage() + "]");
    	}

    }

Now, we’re ready to handle orientation changes. Almost.

Modifying the code alone does not put the Application object into place for our application. We must also tell Android that our class has extended the android.app.Application. This takes place in the AndroidManifest.xml file — specifically as the android:name attribute of the Application tag.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.msi.linuxmag"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:name="PhotoBoothApp">
        <activity android:name=".PhotoBooth2"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Testing the application

Let’s test out the newly modified application by taking a photo. As further evidence that I’ve got lots of junk in my office, I’ll take a photo of an old mouse-pad and mouse:

Take another photo
Take another photo

What happens if we rotate the phone? Before making these code changes, we lost the image. Let’s see what happens now:

Rotation successful!
Rotation successful!

We still have our image!

Another item to consider is this — Android applications don’t casually terminate like a desktop application. If you take a phone call or switch to another application and then return to your application we need to be prepared to restore the state. Storing data in a global place like an Application object can be an appropriate tool for this task.

Trivia time: One of the companies shown in the screen shot released a product named J++. Which one is it? The third person to answer this correctly will receive an electronic copy of Unlocking Android, Second Edition. Send your answer to fableson at msiservices.com.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62