October 31, 2016 / Reading time: 3m

[AndroidBits] Passing data in Android

android

android-bits

A beginners guide to power-up Bundles and Intents.

’em n00bz!

While starting out working with Android one of the major issue developers usually have, is passing data between Activities, Fragments, Services, etc. some get around the problem by allocating data in memory or running for what seems to be the most common solution, moving all variables and/or objects to the Application instance 😵.

Note: I’ve written a small article on how to navigate between your Android components that applies this article’s knowledge but takes it a step further. I do recommend to read this first if you are not familiar with Intent/Bundles.

[AndroidBits] How to decouple navigation between your components
My personal website and blog

How Do They Do It?

The standard way to pass data in Android is using Intents/Bundles. When you’re sending data between Activities/Receivers/Services means this data will be serialized (converted into a byte array) and deserialized (from byte array) once requested. Note that only certain data types are considered Parcelable and they are explicitly spelled out in the Bundle API.

In the destination, you will always have Deep copies of your data/objects.

Practice makes perfect 👍

In order to pass your own custom objects, you have to implement either Serializable (please don’t), or Parcelable, if not, the data for each field must be passed individually which translates into a lot of manual work.

public class Foo {
    private long mId;
    private String mName;
  
    public Foo(long id, String name) {
        mId = id;
        mName = name;
    }
}

The Foo object does not implement the Parcelable interface, there is no way to deserialise or serialise the data. Starting a new Activity while passing the foo’s data:

Foo myExampleFoo = new Foo(1, "Hello world"); // Example object

Intent intent = new Intent(this, CharacterActivity.class);
intent.putExtra("keyFooId", myExampleFoo.getId());
intent.putExtra("keyFooName", myExampleFoo.getName());

If Foo has a Parcelable implementation, archiving the same result is simple

Intent intent = new Intent(this, MyActivity.class);
intent.putExtra("keyFoo", foo);
startActivity(intent);

You can generate a default implementation of Parcelable either on parcelabler.com or use the AS plugin: search for “Android Parcelable”.

Note: order matters in Parcelable, so be sure to keep it right if you make any manual changes:

public class Foo implements Parcelable {

    private long mId;
    private String mName;

    public Foo(long id, String name) {
        mId = id;
        mName = name;
    }


    @Override
    public int describeContents() {
        return 0;
    }

    // This is where you write the values you want to save
    // to the `Parcel`.  
    // The `Parcel` class has methods defined to help you save
    // all of your values.  
    // Note that there are only methods defined for simple 
    // values, lists, and other Parcelable objects.  
    // You may need to make several classes Parcelable to send 
    // the data you want.
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(this.mId);
        dest.writeString(this.mName);
    }

    // Using the `in` variable, we can retrieve the values that 
    // we originally wrote into the `Parcel`.
    // This constructor is usually 
    // private so that only the `CREATOR` field can access.
    protected Foo(Parcel in) {
        this.mId = in.readLong();
        this.mName = in.readString();
    }

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

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

Retrieving data

Accessing the passed data is the same as (literally) getting it from a Bundle. The most important aspect is to use the same keys while accessing the data, no matter what your destination component is.

Activities access the Intent’s data by calling getIntent().getExtras(), this will return a bundle with the passed data.

Bundle passedData = getIntent().getExtras();
long passedId = passedData.getLong("keyFooId");
String fooName = passedData.getString("keyFooString");
Foo foo = new Foo(passedId, passedName);

On the other if you are using fragments passing data, create a Bundle and pass it as the fragment’s arguments:

Bundle args = new Bundle();
args.putString("keySomeValue", "Hello world");
Fragment fragment = new Fragment();
fragment.setArguments(args);

Retrieving this data on the fragment’s instance:

String passedString = getArguments().getString("keySomeValue"));

Alternatives

There are some highly battle tested libraries that make this process easier:

  • EventBus: is an open-source library for Android using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code — simplifying the code, removing dependencies, and speeding up app development.
  • Otto: is an event bus designed to decouple different parts of your application while still allowing them to communicate efficiently. Forked from Guava, Otto adds unique functionality to an already refined event bus as well as specialising it to the Android platform.
  • RxJava: A quick google search will point you to multiple uses of RxJava as a Bus, which wasn’t exactly designed to be used as it, but works. Although I wouldn’t recommend using RxJava just for this functionality.

Share