Android Development – I Wish Someone Had Told Me This

If you’re a developer, you’ve no doubt spent hours chasing down bugs that have you completely baffled.  You change a few things, throw a few outputs, log EVERYTHING, recompile and re-run your test cases only to be greeted by the same NullPointerException.  This weekend I spent some time building the android application for a certain web property and ran into one of those time vampire bugs that should have been obvious, but is never actually stated in any of the documentation that I found.  If you are going to write an Android app, please read this.

Designing your screen layout is incredibly easy.  All layout definitions are done with xml files and they actually make sense to normal people.  Here is an example of a layout file for a simple little screen that has a text box and a button:

main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical" >
             <EditText android:id="@+id/my_text"
                       android:layout_width="fill_parent"
                       android:layout_height="wrap_content"
             <Button android:id="@+id/my_button"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="CLICK ME!!!!111eleven" />
</LinearLayout>

This defines a Linear layout and adds two controls – a text box (my_text) and a button (my_button). In order for this to actually get displayed, we create a java file like this:

MyAwesomeApp.java:

package com.dinstuhl.MyAwesomeApp;

import android.app.Activity;
import android.os.Bundle;

public class MyAwesomeApp extends Activity {

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

Upon startup, your application will start the MyAwesomeApp activity and set the content view (i.e. the screen) to the main.xml file. Go ahead and save it, compile it, start the emulator and then begin watching the first 3 seasons of “Lost”. If the android emulator boots up and loads your app before you want to bitch slap Benjamin Linus, you’re lucky. If it happens before you get to see what is inside the hatch, you’re lying.

You should see a screen with a text box and a button. If not you did something wrong. Let’s just assume that you’ve got a button and a text box. Looks great! But it doesn’t do anything. Let’s re-visit our MyAwesomeApp.java file and I’ll show you where I went wrong:

MyAwesomeApp.java:


package com.dinstuhl.MyAwesomeApp;

import android.app.Activity;
import android.os.Bundle;

public class MyAwesomeApp extends Activity {

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

        //Let's add an onClickEvent to the button!
        Button myButton = (Button) findViewById(R.id.my_button);
        myButton.setOnClickEvent(new OnClickEvent(
          public void onClickEvent(View v){
            //DO SOMETHING AWESOME!
          }
        ));

        setContentView(R.layout.main);
    }
}

Pretty easy, right?

WRONG!

Go ahead and compile this – I’ll wait.

Did you see what happened? You got an error message that basically says, “OH HAI! ADROID DEVELOPMENTS – UR DOIN IT WRONG. XOXO, GOOGLE”. Can you guess what the problem is? Ok, I’ll tell you. Check out the line where we instantiate the button. Instead of declaring a new Button we use the findViewById() method. The argument that we pass is R.id.my_button. What happens is this – there is a java file that is generated based on the contents of your view called R.java. This file creates a reference for every object defined in your main.xml file so that you can get a handle to it at design time. What is NOT obvious is that these objects are not actually created until AFTER the setContentView method has been called. The correct way to do it is like this:

package com.dinstuhl.MyAwesomeApp;

import android.app.Activity;
import android.os.Bundle;

public class MyAwesomeApp extends Activity {

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

        setContentView(R.layout.main); // <-- Do this first or you will make the Baby Jesus cry.

//Let's add an onClickEvent to the button!
Button myButton = (Button) findViewById(R.id.my_button);
myButton.setOnClickEvent(new OnClickEvent(
public void onClickEvent(View v){
//DO SOMETHING AWESOME!
}
));

}
}

So there. Hopefully I’ve just saved you a few hours of screaming. As I learn more I’ll post more. ESPECIALLY pitfalls like this.

  1. Just found your blog, dude. I’ll have to check in more often so I can absorb some coding wisdom. See you around! Going to try to at least drop by the eatup meetup tonight.

    By the way, what are you using for the sexy code/highlighting/scrolling boxes on this post?

  1. No trackbacks yet.