Android App Development For Dummies
Book image
Explore Book Buy On Amazon

"My app needs a broadcast receiver. Can someone remind me how I can code a broadcast receiver? And while you're at it, how does an activity return a result? Oh, heck. Where can I find all that stuff quickly?"

Having examples of the kinds of code used in Android application development ready-to-hand can be a big help. You find plenty of examples right here.

Activities

Here's an activity that starts another activity:

public class CheatSheetActivity extends Activity 
    implements OnClickListener {
  Button button1;
  static final String MY_ACTION = "com.allmycode.action";
  static final String MY_URI 
                     = "my_scheme:my_scheme_specific_part";
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    button1 = (Button) findViewById(R.id.button1);
    button1.setOnClickListener(this);
  }
  public void onClick(View arg0) {
    Intent intent = new Intent();
    intent.setAction(MY_ACTION);
    intent.setData(Uri.parse(MY_URI));
    startActivity(intent);
  }
}

And don't forget — when you create a new activity class, you must add a corresponding element to your AndroidManifest.xml file. The OtherActivity element's intent filter looks something like this:

<intent-filter>
  <action android:name="com.allmycode.action" />
  <category 
      android:name="android.intent.category.DEFAULT" />
  <data android:scheme="my_scheme" />
</intent-filter>

To get a result from an activity, add the following code (or something resembling it) to your app:

final int CODE_NUMBER = 42;
final String CLASSNAME = "CheatSheetActivity";
public void onClick(View arg0) {
  Intent intent = new Intent();
  intent.setAction(MY_ACTION);
  intent.setData(Uri.parse(MY_URI));
  startActivityForResult(intent, CODE_NUMBER);
}
protected void onActivityResult
    (int codeNumber, int resultCode, Intent intent) {
  if (resultCode == RESULT_OK) {
    if (codeNumber == CODE_NUMBER) {
      Log.i(CLASSNAME, 
          intent.getData().getSchemeSpecificPart());
    }
  }
}

And in the activity that creates the result, add code of the following kind:

Intent intent = new Intent();
intent.setData(Uri.parse("result:hello"));
setResult(RESULT_OK, intent);
finish();

Services

A service typically does its work without bothering (or even notifying) the user. For example, a stock price service might reach out to the web and get the latest prices of the user's favorite picks. Another app's activity might get data from the service and display the data on the device's screen.

The following code is a complete (but not very accurate) weather service:

public class MyWeatherService extends Service {
  Messenger messengerToClient = null;
  MyIncomingHandler myIncomingHandler =
      new MyIncomingHandler();
  Messenger messengerToService = 
      new Messenger(myIncomingHandler);
  @Override
  public IBinder onBind(Intent intent) {
    return messengerToService.getBinder();
  }
  class MyIncomingHandler extends Handler {
    @Override
    public void handleMessage(Message incomingMessage) {
      messengerToClient = incomingMessage.replyTo;
      Bundle reply = new Bundle();
      reply.putString("weather", "It's dark at night.");
      Message replyMessage = Message.obtain();
      replyMessage.setData(reply);
      try {
        messengerToClient.send(replyMessage);
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    }
  }
}

In another package, you put the code to call the weather service. Here's some sample code:

public class ServiceConsumerActivity extends Activity
    implements OnClickListener {
  Messenger messengerToService = null;
  MyIncomingHandler myIncomingHandler =
      new MyIncomingHandler();
  Messenger messengerFromService = 
      new Messenger(myIncomingHandler);
  ServiceConnection connection =
      new MyServiceConnection();
  SharedPreferences prefs;
  boolean isBound = false;
  void bind() {
    Intent intent = new Intent();
    intent.setAction("com.allmycode.WEATHER");
    isBound =
        bindService(intent, connection,
            Context.BIND_AUTO_CREATE);
  } 
  public void queryService() {
    if (isBound) {
      Bundle bundle = new Bundle();
      bundle.putString("location", "Philadelphia");
      Message message = Message.obtain();
      message.replyTo = messengerFromService;
      message.setData(bundle);
      try {
        messengerToService.send(message);
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    } else {
      textView1.setText(R.string.service_not_bound);
    }      
  }
  class MyIncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
      Bundle bundle = msg.getData();
      textView1.setText(bundle.getString("weather"));
    }
  } 
  void unbind() {
    if (isBound) {
      unbindService(connection);
      isBound = false;
    }
  }
  class MyServiceConnection implements ServiceConnection {
    public void onServiceConnected(
        ComponentName className, IBinder binder) {
      messengerToService = new Messenger(binder);
    }
    public void onServiceDisconnected(ComponentName n) {
      messengerToService = null;
    }
  }
  // I don't include the onCreate method or the 
  //   onClick method in this example.
}

Of course, no app survives without some elements in the manifest file. To register this section's service, you need an element of the following kind:

<service android:name=".MyWeatherService">
  <intent-filter>
    <action android:name="com.allmycode.WEATHER" />
  </intent-filter>
</service>

Broadcast receivers

When you do a broadcast, you fling an intent out into the wild. Broadcast receivers with compatible intent filters wake up and do something useful with the broadcast information. (After doing something with the broadcast info, the receiver goes back to sleep. In my next incarnation, I want to be a broadcast receiver.)

To create your own broadcast receiver, you extend Android's BroadcastReceiver class and you declare an onReceive method. For example, the following code responds to matching broadcasts:

public class MyReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Do important stuff
  }
}

Another app creates a broadcast with code of the following sort:

Intent intent = new Intent();
intent.setAction("com.allmycode.ACTION");
sendBroadcast(intent);

You can register a broadcast receiver in your AndroidManifest.xml file:

<receiver android:name=".MyReceiver">
  <intent-filter>
    <action android:name="com.allmycode.ACTION" />
  </intent-filter>
</receiver>

For more flexibility, you can register a receiver in your Java code. The following Java code does essentially what the element in an AndroidManifest.xml file does:

IntentFilter filter = new IntentFilter();
filter.addAction("com.allmycode.ACTION");
registerReceiver(new MyReceiver(), filter);

Content providers

An app's content provider makes data available to other apps that run on the same device. The provider's interface resembles a database's interface, with tables, rows, cursors, and all that good stuff. For example, the code to query a content provider looks like this:

public Cursor query(Uri uri, String[] columns,
    String whereClause, String[] whereArgs,
    String sortOrder) {
  Cursor cursor = null;
  int code = uriMatcher.match(uri);
  if (code == 1) {
    cursor =
        db.query(SIMPLETABLE, columns, whereClause,
            whereArgs, null, null, sortOrder);
  } else if (code == 2) {
    String[] columnNames = { "_id", "name", "amount" };
    String[] rowValues = { "Table ", "4 ", "2" };
    MatrixCursor matrixCursor =
                         new MatrixCursor(columnNames);
    matrixCursor.addRow(rowValues);
    cursor = matrixCursor;
  }
  return cursor;
}

Fragments

A fragment is like a view — a visible thing that you can display inside an activity. But unlike a view, a fragment has its own lifecycle methods. So Android can create a little stack of fragments inside an activity. When the user presses the Back button, Android pops a fragment off of the stack. (If there are no fragments to pop, Android pops the entire activity off the task stack.)

You can put a fragment into the following frame layout:

<FrameLayout android:id="@+id/docs"
    android:layout_height="match_parent"
    android:layout_width="0px" 
    android:layout_weight="1"
    android:background=
      "?android:attr/detailsElementBackground" />

To put a fragment into the layout, you perform a fragment transaction. Here's what a fragment transaction looks like:

DocsFragment docsFragment = DocsFragment.newInstance(index);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction =
                         fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.docs, docsFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

Intents and Intent Filters

When an intent meets the right intent filter, the result is a match made in heaven. But the rules for matching intents with filters are complicated. The rules read like the legal clauses in a prenuptial agreement.

You can use Java methods to describe an intent. Here are some often-used methods:

  • setAction: Sets the intent's action. (An intent can have only one action.)

  • addCategory: Adds a category to the intent. (An intent can have many categories.)

  • setData: Sets the intent's URI, and removes the intent's MIME type (if the intent has a MIME type).

  • setType: Sets the intent's MIME type and removes the intent's URI (if the intent has a URI).

  • setDataAndType: Sets both the intent's URI and the intent's MIME type. According to the docs, "This method should very rarely be used."

You can also use XML code to describe an intent.

<action android:name="string" />
<category android:name="string" />
<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string" />

In the URI http://www.allmycode.com:80/android, the scheme is http, the host is www.allmycode.com, the port is 80, and the path is android. The authority (which isn't one of the attributes in a element, but is useful to know about) is www.allmycode.com:80.

You typically set an intent filter's values in the AndroidManifest.xml file. But in Java code, the android.content.IntentFilter class has lots of useful methods. Here are a few of them:

  • addAction: Adds an action to the filter.

  • addCategory: Adds a category to the filter.

  • addDataScheme: Adds a scheme to the filter.

  • addDataAuthority: Adds an authority to the filter.

  • addDataPath: Adds a path to the filter.

  • addDataType: Adds a MIME type to the filter.

An intent filter can have many actions, many categories, and so on.

Here's a brief list of requirements for a match between an intent and an intent filter. This list isn't complete so, if you want a complete list, you better buy Android Application Development All-in-One For Dummies by Barry Burd.

  • If an intent has an action, in order to match the intent, an intent filter must have an identical action. The intent filter can have additional actions. Any of these additional actions have no effect on the match.

  • If an intent has categories, in order to match the intent, an intent filter must have these (and possibly more) categories.

  • If an intent has a MIME type, in order to match the intent, an intent filter must have a matching MIME type. The intent filter can have additional MIME types. Any of these additional MIME types have no effect on the match.

  • If an intent filter has MIME types, in order to match the intent filter, an intent must have a MIME type and the intent's MIME type must match one of the filter's MIME types.

  • To a limited extent, the matching of MIME types can involve wildcards and regular expressions.

  • If an intent has a URI scheme, in order to match the intent, an intent filter must have a matching URI scheme.

  • If an intent filter has URI schemes, in order to match the intent filter, an intent must have a URI scheme and the intent's URI scheme must match one of the filter's URI schemes.

To finish this list, copy the last two rules, changing a word or two in each of the copies:

  • If an intent has a URI host, in order to match the intent, an intent filter must have a matching URI host.

  • If an intent filter has URI hosts, in order to match the intent filter, an intent must have a URI host and the intent's URI host must match one of the filter's URI hosts.

  • If an intent has a URI port, in order to match the intent, an intent filter must have a matching URI port.

  • If an intent filter has URI ports, in order to match the intent filter, an intent must have a URI port and the intent's URI port must match one of the filter's URI ports.

  • If an intent has a URI path, in order to match the intent, an intent filter must have a matching URI path.

  • If an intent filter has URI paths, in order to match the intent filter, an intent must have a URI path and the intent's URI path must match one of the filter's URI paths.

About This Article

This article can be found in the category: