Discussing the nuts and bolts of software development

Tuesday, February 01, 2011

 

Pass Me the Data, by Rob Woods

You've written the Hello Android app, then created your own simple app. Now you've created a much more complicated app with several activities. You may need to be able to pass data between your activities. In this post, I will take you from the basics of passing simple data types between activities, right up to making your own custom objects parcelable so they can also be passed along with the same ease as a simple data type.
If you feel that you need a bit more background on this subject matter before reading further, here is a helpful article on Android application development.

Before we start passing data around, first you will need to understand how an activity is launched. To launch a new activity, you would create an Intent and pass it to the Activity method startActivity().

Illustration:
"Intent newIntent = new Intent(Context, Class);
startActivity(newIntent);"

Now lets say you have a listactivity, and whenever a user taps on an entry in the list, you want to launch a new activity that does something with the data from that list entry. The most basic scenario for passing data is to attach an extra to the intent in the form of

"newIntent.putExtra(name,value);"

Where name is a String that is used to "tag" your data and value is the actual data object you are passing to the intended activity. Value can be many different types, including String, int, Boolean, etc. You can see the complete list of putextra methods here.

Here's an example from the callingaActivity.
"Intent newIntent = new Intent(this, SomeActivity.class);
newIntent.putExtra("MAGIC_NUMBER", 42);
startActivity(newIntent);"

Now on the called activity side (receiving the Intent), in your activities onCreate() method, you will need to retrieve the extra data from the intent. You can do this by calling getIntent() to get the intent that started the activity, then getIntExtra(name, default). This is if you passed an int value, if you passed a different type, you would use the corresponding method for that type.

For example:
"int magic_number = getIntent().getIntExtra("MAGIC_NUMBER", 420);"

Note that the default value is used if the tag "MAGIC_NUMBER" had no value assigned to it. This is great if you are just passing one piece of data using a basic data type, but if you are passing more data than just a single type, you might want to consider using Bundle.

Basically, a Bundle is just a mapping of tag-data pairs grouped together into one passable object. Let's say that you had a contact list and when the user taps a contact, you want to pass the name, id and phone number to the called Activity. You could do so like this:

"Intent newIntent = Intent(this, ContactDetails.class);
Bundle extras = new Bundle();
extras.putString("CONTACT_NAME", name);
extras.putInt("CONTACT_ID", id);
extras.putString("CONTACT_NUMBER", number);
newIntent.putExtras(extras);
startActivity(newIntent);:

And on the receiving Activity:

"Bundle extras = getIntent().getExtras();
String name = extras.getString("CONTACT_NAME");
int id = extras.getInt("CONTACT_ID");
String number = extras.getString("CONTACT_NUMBER");"

Now lets say you've created your own data class with several different types of data representing the class:

"public class Dog extends Object {
private String mType;
private String mName;
private int mId;

public Dog(String type, String name, int id) {
mType = type;
mName = name;
mId = id;
} "

You could pass each piece of data separately by adding 4 tag-data pairs to a Bundle and passing the Bundle. However, a smarter way of doing this is to implement the parcelable interface in your Dog class. The parcelable interface has 2 methods which are needed to implement, describeContents() and writeToParcel(Parcel, int).

"@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mType);
dest.writeString(mName);
dest.writeInt(id);
}"

Using this method, you must add each of your data types to the Parcel with the correct write method for its data type.

If your class has child classes, you can use the describeContents() method to differentiate between child classes so that when you unparcel your parceled object, you can create the correct child object.

This is only half of the solution. Now that you have the facilities to write your object to a parcel, you also need to be able to rebuild your object in the called activity. First you will need a new constructor for your class. One that takes a parcel object and can populate it's data members from it;

'public Dog(Parcel dest) {
mType = dest.readString();
mName = dest.readString();
mId = dest.readInt();
}'

Note the order.
You must read your data types from your parcel in the same order you wrote them. Essentially you're flattening your data into one data stream, then reading it back on the other side.

Finally, you will need to create a parcelable creator in your class that will trigger your new parcelable constructor when needed:

"public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public LocalContact createFromParcel(Parcel source) {
return new LocalContact(source);
}

@Override
public LocalContact[] newArray(int size) {
return new LocalContact[size];
}
};"

One last thing I'll leave you with is the concept of chaining your parcelable objects. Lets say your Dog class has a custom object as one of its data members. You need to make sure that class also implements the parcelable interface. In order to support parcelizing your custom object inside your Dog class, you will need to change 2 methods.
First, Dog's writeToParcel(Parcel, int) method needs to tell your custom object to write itself to the parcel.
To do this, we can call writeParcelable(Object, int). This method will invoke your custom classes writeToParcel method:

"@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mType);
dest.writeString(mName);
dest.writeInt(id);
dest.writeParcelable(dogTag, flags);
}"

Here dogTag would be your custom class that could implement say, a phone number and address for where the dog lives. Second Dog's parcel constructor. Here we will call readParcelable(Classloader) to get the custom objects parceled data:

"public Dog(Parcel dest) {
mType = dest.readString();
mName = dest.readString();
mId = dest.readInt();
dogTag = dest.readParcelable(DogTag.class.getClassLoader());
}"

This should get you passing your data around to your activities!

Labels: , , , , , , , , , , , , ,


Comments: Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?