Bundle Injection with Dagger

Benoît Quenaudon
3 min readSep 7, 2017

We’ll be using Dagger here but I believe other library can offer the same functionality.

In Android, we can pass data to activities via the intent, specifically its extras. We can also take advantage of the savedInstanceState bundle for restoring our view after it’s been discarded. If we want to use the data contained inside the bundle, we would need to extract it in a context bound environment, keep it and pass it over to other members as needed.

public class MyActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
String myValue = intent.getStringExtra(“MY_KEY”);
// keep it and pass it around to needed instances.
}
}

The data is not accessed when needed, it’s accessed when it is possible to do so. That is not a natural way to think about it, can make the code hard to understand, and having to carry data around is not a comfortable way to build an app. How about we fix this?

Let’s first create a bundle service which will be responsible for storing and providing those bundle’s data.

public class BundleService {
private final Bundle data;
public BundleService(Bundle savedState, Bundle intentExtras) {
data = new Bundle();
if (savedState != null) {
data.putAll(savedState);
}
if (intentExtras != null) {
data.putAll(intentExtras);
}
}
public Object get(String key) {
return data.get(key);
}
}

Next, we would create a base class for all activities as so

public abstract class BaseActivity extends Activity {
private BundleService bundleService;
@Override public void onCreate(Bundle savedInstanceState) {
bundleService = new BundleService(
savedInstanceState,
getIntent().getExtras());

super.onCreate(savedInstanceState);
}
@Override protected void onSaveInstanceState(Bundle outState) {
outState.putAll(bundleService.getAll());
super.onSaveInstanceState(outState);
}
public BundleService getBundleService() {
return bundleService;
}
}

We first instantiate our bundle service inside onCreate, give it all the data we may have, without forgetting to store all the bundle’s data as well in the outState inside onSaveInstanceState in the case the activity gets discarded.

Now enter Dagger.

The configuration needed for the injection is simple and needs to be done only once. We create a bundle module that’ll be responsible for providing the data we’ll be injecting and we add it into the activity’s module’s dependency.

@Module public abstract class BundleModule {
@Provides @ScopeActivity
static BundleService provideBundleService(BaseActivity context) {
return context.getBundleService();
}
}
@Module public abstract class ApplicationModule {
@ScopeActivity
@ContributesAndroidInjector(modules = {
OutputActivityModule.class,
BundleModule.class
})
abstract OutputActivity contributeOutputActivityInjector();
}

The main configuration is done but as of now, we cannot inject anything relevant. For each and every data to be injected we will create a qualifier which will be used to identify the data we want to inject. We create a provider method inside the bundle module and we’re good to go.

@Qualifier @Retention(RUNTIME) public @interface SomeInput {}@Module public abstract class BundleModule {
public static final String EXTRA_SOME_INPUT = "MY_KEY";
@Provides @ScopeActivity
static BundleService provideBundleService(BaseActivity context) {
return context.getBundleService();
}
@Provides @ScopeActivity @SomeInput
static String provideSomeInput(BundleService bundleService) {
String style = (String) bundleService.get(EXTRA_SOME_INPUT);
return style == null ? "Null" : style;
}

}

Now, we can inject the field qualified @SomeInput anywhere, as long as we’re inside the scope of our activity.

class OutputPresenter {
private final OutputView view;
private final String inputText;
@Inject OutputPresenter(OutputView view,
@SomeInput String inputText) {
this.view = view;
this.inputText = inputText;
}
void onStart() {
view.showText(inputText);
}
}

That’s it. This way, we do not need to manually extract the data inside the OutputActivity and carry it around or inject it manually anywhere. We can forget about managing manually our bundles, just inject what’s needed where needed and Dagger does the rest!

There is a basic sample project implementing this pattern on GitHub for those who’d want to play with the code: https://github.com/oldergod/DaggerBundleInjectionDemo

ps: instead of using creating a qualifier interface per data, we can use the @Named annotation and pass it a string, like the key we used to store the data into the bundle, as a key.

--

--