Validate your Bundles

One of my pain points with Android development is the bundle system. The bundles by them self are really useful and they are the core of Androids way of sharing data between activities and app and for that they work fine.

The problem I have with them is that you can’t subclass them and export interfaces that actually makes sense. It would be nice if we could use bundle.setTitle(“My Title”);
 instead of 
bundle.setString(packageName + “.TITLE”, “My Title”);

I guess that may people have the same problem and I also assume that a lot of people have their own way of fixing this, but in any case I want to show my solution.

Since bundles can’t be subclassed we must use a wrapper or factory. This of course either means a static Class or creating a new object that will take heap memory and sooner or later be released via the garbage collector and of course take some time to execute, what should we use?

Since bundles normally is used when you are doing operations that anyway will handle a lot of memory like starting Activities, handling unloaded and reloaded Activities, communicating with services or other apps I opted for creating new objects. This helps with testing and if you like you can create base bundles for common parameters and extend that to more special ones later on.

Since Intents internally uses Bundles to communicate data it seems logical to add functionality to add extras data directly into the bundle wrapper.

package com.nordicusability.jiffy.bundles;

import android.content.Intent;
import android.os.Bundle;

public abstract class BundleWrapper {

public BundleWrapper() {
}

public BundleWrapper(Bundle bundle) {
validate(bundle);
}

public Bundle build() {
Bundle bundle = new Bundle();
return doAppend(bundle);
}

public Intent append(Intent intent) {
return intent.putExtras(doAppend(new Bundle()));
}

private Bundle doAppend(Bundle bundle) {
append(bundle);
validate(bundle);
return bundle;
}

private void validate(Bundle bundle) {
if (!isValid(bundle)) {
throw new RuntimeException("Not a valid bundle");
}
}

protected abstract boolean isValid(Bundle bundle);

protected abstract Bundle append(Bundle bundle);
}

(You should of course create your own Exception class, but I didn’t want to add to much code here)

The RuntimeException for the bundle wrapper might seem a bit harsh, but during development its perfect since I rather crash here than in the Activity later on.

Now when we have this base class we can subclass it to make use of the new interface. To show this I created a small class that keeps a time-stamp and just validates that it is set and to a positive value

package com.nordicusability.jiffy.bundles;

import android.os.Bundle;

public class ExampleBundle extends BundleWrapper {

private final String EXTRAS_TIME = "com.nordicusability.jiffy.extras.TIME";
private final long timeMs;

public ExampleBundle(long timeMS) {
this.timeMs = timeMS;
}

public ExampleBundle(Bundle bundle) {
super(bundle);
timeMs = bundle.getLong(EXTRAS_TIME);
}

@Override
protected boolean isValid(Bundle bundle) {
return bundle.containsKey(EXTRAS_TIME)
&& bundle.getLong(EXTRAS_TIME, -1) > 0;
}

@Override
protected Bundle append(Bundle bundle) {
bundle.putLong(EXTRAS_TIME, timeMs);
return bundle;
}

public long getTime() {
return timeMs;
}
}

If we now want to use this as a transport of a selected value from one activity to another we just use it to wrap the bundle and then append it to the intent.

private void startDialogActivity(){
long timeStamp = System.currentTimeMillis();
Intent intent = new Intent(this, DialogActivity.class);
new ExampleBundle(timeStamp).append(intent);
startActivity(intent);
}

And in the receiving activity you can unwrap the data with the same technique.

protected void onCreate(Bundle savedInstanceState) {
ExampleBundle bundledData;
if(savedInstanceState == null){
bundledData = new ExampleBundle(getIntent().getExtras());
} else {
bundledData = new ExampleBundle(savedInstanceState);
}
setSelectedTime(bundledData.getTime());
}

protected void onSaveInstanceState(Bundle outState) {
new ExampleBundle(getSelectedTime()).append(outState);
}

As a bonus, since it’s wrapping a standard bundle we can reuse it for saving states when the activity is unloaded, for example when the screen rotates.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.