Several months ago, I was faced with a problem of passing a complex object to another Activity. There are several ways of doing this:
- “Deconstructing” the complex object to simple data types and passing them as extras through
putExtra()
- Making the object
Parcelable
- Making the object
Serializable
I don’t really understand the concepts behind an object being Parcelable
or Serializable
, so I was not comfortable using that approach. Deconstructing the object, on the other hand, is easier but it is quite hard to manage as the number of fields in the object increases.
And then I came across another solution: using Bundle
s. In this post, I will try to explain how to do that.
In this sample project, we want to pass an instance of an AttendeeObject
from our main activity to another. To better illustrate how we can use this method for different data types, an AttendeeObject
will hold three pieces of information: a String
name, an Integer
age, and a boolean
indicating the person’s presence.
First, we construct the AttendeeObject
like any old POJO. Create all the fields you want the object to contain and generate getters and setters for each. We then create the methods we would need to be able to pass, and subsequently receive, this object between activities.
To pass an AttendeeObject
, we would need to put it in a Bundle
and pass it using Intent#putExtra(String key, Bundle bundle).
public Bundle bundleAttendee(AttendeeObject attendee){
Bundle bundle = new Bundle();
bundle.putString(NAME, attendee.getName());
bundle.putInt(AGE, attendee.getAge());
bundle.putBoolean(PRESENCE, attendee.isPresent());
return bundle;
}
Here, we simply put into a Bundle
the values in the object that we want to pass.
Next, we should be able to convert this bundle back to an AttendeeObject
in the receiving Activity
. This task is done by the following method:
public AttendeeObject unBundleAttendee(Bundle attendeeBundle){
AttendeeObject attendee = new AttendeeObject();
attendee.setName(attendeeBundle.getString(NAME));
attendee.setAge(attendeeBundle.getInt(AGE));
attendee.setIsPresent(attendeeBundle.getBoolean(PRESENCE));
return attendee;
}
Now, we are ready to use this in our application. I created a simple app that asks the user for the three values we would need to populate AttendeeObject
.
I won’t discuss the details of creating the layout file, but it might be worthy to note that the possible values for the Spinner are just “Yes” and “No”.
When the user clicks on the “Create Attendee” button, the application creates a new AttendeeObject
based on the values in the form and adds this to an ArrayList
of AttendeeObject
s.
private void onCreateAttendee() {
// Create a new AttendeeObject
AttendeeObject attendee = new AttendeeObject();
attendee.setName(mNameText.getText().toString());
attendee.setAge(Integer.valueOf(mAgeText.getText().toString()));
attendee.setIsPresent(mCurrentPresence);
// You can then do whatever you want with this object
// like adding it to an ArrayList or saving it to a database
mAttendees.add(attendee);
Toast.makeText(ComplexObjectDemo.this, "Attendee added!", Toast.LENGTH_SHORT).show();
}
When the user clicks on the “Submit Attendee” button, the app “packages” the chosen value into a Bundle
and sets it as an extra
to the Intent
. For illustration purposes, the application always gets the second AttendeeObject
in the ArrayList
.
private void onSubmitObject() {
Intent intent = new Intent(ComplexObjectDemo.this, ComplexObjectReceiverDemo.class);
// For simplicity, we will always get the second value
Bundle value = createAttendeeBundle(1);
// In a "real" application, the Key should be defined in a constants file
intent.putExtra("test.complex.attendee", value);
startActivity(intent);
}
private Bundle createAttendeeBundle(int index) {
// Here, mAttendee is an instance of AttendeeObject
// and mAttendees is an ArrayList holding all the created attendees
return mAttendee.bundleAttendee(mAttendees.get(index));
}
Upon receiving the Intent
, the new activity simply displays the values in the AttendeeObject
passed into it. Here’s how we can “unbundle” the Bundle
:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_complex_demo_receiver);
// Get the extras passed to this Activity
setUpViews(getIntent().getExtras());
}
private void setUpViews(Bundle extras) {
// Again, the key should be in a constants file!
Bundle extraAttendee = extras.getBundle("test.complex.attendee");
AttendeeObject attendee = new AttendeeObject();
// Deconstruct the Bundle into the AttendeeObject
attendee = attendee.unBundleAttendee(extraAttendee);
// The AttendeeObject fields are now populated, so we set the texts
((TextView) findViewById(R.id.attendee_age_value))
.setText(String.valueOf(attendee.getAge()));
((TextView) findViewById(R.id.attendee_name_value))
.setText(attendee.getName());
((TextView) findViewById(R.id.attendee_presence_value))
.setText(String.valueOf(attendee.isPresent()));
}
Here’s how the receiving activity looks like:
Here’s the complete AttendeeObject
class.
`AttendeeObject` code
```java package droidista.example; import android.os.Bundle; public class AttendeeObject { public static final String NAME = "attendee.name"; public static final String AGE = "attendee.age"; public static final String PRESENCE = "attendee.presence"; private String mName; private int mAge; private boolean mIsPresent; public AttendeeObject() { } public Bundle bundleAttendee(AttendeeObject attendee) { Bundle bundle = new Bundle(); bundle.putString(NAME, attendee.getName()); bundle.putInt(AGE, attendee.getAge()); bundle.putBoolean(PRESENCE, attendee.isPresent()); return bundle; } public AttendeeObject unBundleAttendee(Bundle attendeeBundle) { AttendeeObject attendee = new AttendeeObject(); attendee.setName(attendeeBundle.getString(NAME)); attendee.setAge(attendeeBundle.getInt(AGE)); attendee.setIsPresent(attendeeBundle.getBoolean(PRESENCE)); return attendee; } public String getName() { return mName; } public void setName(String name) { this.mName = name; } public int getAge() { return mAge; } public void setAge(int age) { this.mAge = age; } public boolean isPresent() { return mIsPresent; } public void setIsPresent(boolean isPresent) { this.mIsPresent = isPresent; } } ```And our main activity:
`MainActivity` code
```java package droidista.example; import java.util.ArrayList; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import android.widget.AdapterView.OnItemSelectedListener; public class ComplexObjectDemo extends Activity implements OnClickListener { private Button mGoToNextIntent, mCreateAttendee, mClearFields; private EditText mNameText, mAgeText; private Spinner mPresence; AttendeeObject mAttendee = new AttendeeObject(); private boolean mCurrentPresence; private ArrayList<AttendeeObject> mAttendees = new ArrayList<AttendeeObject>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_complex_demo); setUpViews(); } private void setUpViews() { setUpSpinner(); mNameText = (EditText)findViewById(R.id.name_text_input); mAgeText = (EditText)findViewById(R.id.age_text_input); mGoToNextIntent = (Button)findViewById(R.id.btn_submit_object); mGoToNextIntent.setOnClickListener(this); mCreateAttendee = (Button)findViewById(R.id.btn_create_attendee); mCreateAttendee.setOnClickListener(this); mClearFields = (Button) findViewById(R.id.btn_clear_fields); mClearFields.setOnClickListener(this); } private void setUpSpinner() { mPresence = (Spinner) findViewById(R.id.presence_spinner); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( this, R.array.is_present_values, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mPresence.setAdapter(adapter); } private Bundle createAttendeeBundle(int index) { // Here, mAttendee is an instance of AttendeeObject // and mAttendees is an ArrayList holding all the created attendees return mAttendee.bundleAttendee(mAttendees.get(index)); } public void onClick(View v) { switch(v.getId()){ case R.id.btn_create_attendee: onCreateAttendee(); break; case R.id.btn_submit_object: onSubmitObject(); break; case R.id.btn_clear_fields: onClearFields(); break; } } private void onClearFields() { mAgeText.setText(""); mNameText.setText(""); mPresence.setSelection(0); } private void onSubmitObject() { // For simplicity, we will always get the second value Bundle value = createAttendeeBundle(1); // In a "real" application, the Key should be defined in a constants file Intent intent = new Intent(ComplexObjectDemo.this, ComplexObjectReceiverDemo.class); intent.putExtra("test.complex.attendee", value); // Start the new activity startActivity(intent); } private void onCreateAttendee() { // Create a new AttendeeObject AttendeeObject attendee = new AttendeeObject(); attendee.setName(mNameText.getText().toString()); attendee.setAge(Integer.valueOf(mAgeText.getText().toString())); attendee.setIsPresent(mCurrentPresence); // You can then do whatever you want with this object // like adding it to an ArrayList or saving it to a database mAttendees.add(attendee); // Notify the user Toast.makeText(ComplexObjectDemo.this, "Attendee added!", Toast.LENGTH_SHORT).show(); } public class MyOnItemSelectedListener implements OnItemSelectedListener { public void onItemSelected(AdapterView parent, View view, int pos, long id) { mCurrentPresence = (pos==0) ? true : false; } public void onNothingSelected(AdapterView parent) { /*Do nothing.*/ } } } ```And here’s the second activity: