Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up dependency injection using Dagger for things other than Activities and Fragments?

I started setting up dependency injection using Dagger as follows. Please feel encouraged to correct my implementation since I might have mistakes in there! The implementation follows the android-simple example provided by the project. In the following you can see how I successfully added dependency injection for Activities and Fragments. I try to keep it easy for now so I decided to inject Timber as a logger substitution for Android's log util.

import android.app.Application; import java.util.Arrays; import java.util.List; import dagger.ObjectGraph; import com.example.debugging.LoggingModule;  public class ExampleApplication extends Application {      private ObjectGraph mObjectGraph;      protected List<Object> getModules() {         return Arrays.asList(                 new AndroidModule(this),                 new ExampleModule(),                 new LoggingModule()         );     }      private void createObjectGraphIfNeeded() {         if (mObjectGraph == null) {             Object[] modules = getModules().toArray();             mObjectGraph = ObjectGraph.create(modules);         }     }      public void inject(Object object) {         createObjectGraphIfNeeded();         mObjectGraph.inject(object);     } } 

By now the AndroidModule is not used anywhere it but might be helpful when a Context and LayoutInflater is needed e.g. in CursorAdapters.

import android.content.Context; import android.view.LayoutInflater; import javax.inject.Singleton; import dagger.Module; import dagger.Provides;  /**  * A module for Android-specific dependencies which require a {@link Context}   * or {@link android.app.Application} to create.  */ @Module(library = true) public class AndroidModule {     private final ExampleApplication mApplication;      public AndroidModule(ExampleApplication application) {         mApplication = application;     }      /**      * Allow the application context to be injected but require that it be       * annotated with {@link ForApplication @Annotation} to explicitly       * differentiate it from an activity context.      */     @Provides @Singleton @ForApplication Context provideApplicationContext() {         return mApplication;     }      @Provides @Singleton LayoutInflater provideLayoutInflater() {         return (LayoutInflater) mApplication             .getSystemService(Context.LAYOUT_INFLATER_SERVICE);     } } 

I am not sure what application-specific providers would go here. I stay with logging for now.

import dagger.Module;  @Module(         complete = false ) public class ExampleModule {     public ExampleModule() {          // TODO put your application-specific providers here!     } } 

I prepared LoggingModule which provides access to Timber.

package com.example.debugging;  import javax.inject.Singleton;  import dagger.Module; import dagger.Provides; import com.example.BuildConfig; import com.example.activities.BaseFragmentActivity; import com.example.activities.DetailsActivity; import com.example.fragments.BaseListFragment; import com.example.fragments.ProfilesListFragment; import timber.log.Timber;  @Module(injects = {         // Activities         BaseFragmentActivity.class,         DetailsActivity.class,         // Fragments         BaseListFragment.class,         ProfilesListFragment.class }) public class LoggingModule {      @Provides @Singleton Timber provideTimber() {         return BuildConfig.DEBUG ? Timber.DEBUG : Timber.PROD;     } } 

The base class for Activities injects itself into the object graph ...

package com.example.activities;  import android.os.Bundle;     import com.actionbarsherlock.app.SherlockFragmentActivity;     import javax.inject.Inject; import com.example.ExampleApplication; import timber.log.Timber;  public abstract class BaseFragmentActivity extends SherlockFragmentActivity {      @Inject Timber mTimber;      @Override     protected void onCreate(Bundle savedInstanceState) {         // ...         super.onCreate(savedInstanceState);         ((ExampleApplication) getApplication()).inject(this);     } } 

... and any sub class benefits from Timber being already present.

package com.example.activities;  import android.os.Bundle; import com.example.R;  public class DetailsActivity extends BaseFragmentActivity {      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_details);         mTimber.i("onCreate");         // ...     } } 

Same for Fragments: the base class does the dirty job ...

package com.example.fragments;  import android.os.Bundle; import com.actionbarsherlock.app.SherlockListFragment; import javax.inject.Inject; import com.example.ExampleApplication; import timber.log.Timber;  public abstract class BaseListFragment extends SherlockListFragment {      @Inject Timber mTimber;      @Override     public void onActivityCreated(Bundle savedInstanceState) {         super.onActivityCreated(savedInstanceState);         ((ExampleApplication) getActivity().getApplication()).inject(this);     }  } 

... and the sub class benefits from its super class.

package com.example.fragments;  import android.os.Bundle;  public class ProfilesListFragment extends BaseListFragment {      @Override     public void onActivityCreated(Bundle savedInstanceState) {         super.onActivityCreated(savedInstanceState);         // TODO This might be a good example to inject resources         // in the base class. But how?         setEmptyText(getResources()            .getString(R.string.profiles_list_no_content));         mTimber.i("onActivityCreated");         // ...     }  } 

So far so good. But how can inject Timber into BaseCursorAdapter, BaseContentProvider, BaseSQLiteOpenHelper, BaseService, BaseAsyncTask and static helper methods?

The deprecated android example by Christopher Perry points out how to inject an Adapter into a ListFragment but not how to inject Context, Resources, LayoutInflater, Cursor into the (Cursor)Adapter or just Timber.


References:

  • Dagger
  • android-simple example
  • Jesse Wilson - Dagger: A Fast Dependency Injector for Android and Java
  • Eric Burke - Android App Anatomy
like image 993
JJD Avatar asked Sep 20 '13 01:09

JJD


People also ask

How do you add a dagger dependency?

In order to use dependency injection with the help of dagger 2 libraries, we need to add it's dependency. Go to Gradle Scripts > build. gradle(Module: app) and add the following dependencies. After adding these dependencies you need to click on Sync Now.

How many ways can you inject dependency?

Types of Dependency Injection The injector class injects dependencies broadly in three ways: through a constructor, through a property, or through a method. Constructor Injection: In the constructor injection, the injector supplies the service (dependency) through the client class constructor.

How does a dagger injection work?

Dagger automatically generates code that mimics the code you would otherwise have hand-written. Because the code is generated at compile time, it's traceable and more performant than other reflection-based solutions such as Guice. Note: Use Hilt for dependency injection on Android.


1 Answers

Check out Andy Dennie's examples for injecting in different scenarios:

https://github.com/adennie/fb-android-dagger

Some points where I inject:

  • Activity, Service, and Fragment subclasses: in onCreate
  • BroadcastReceiver subclasses (includes, e.g. AppWidgetProvider): in onReceive
like image 175
orip Avatar answered Oct 08 '22 11:10

orip