1

I read Christian Gruber's post and I started wondering how to use Application-wide Singletons.
In my application I have a class DBHelper, which main purpose is holding the keys to my database. I have also many (at least two) different DAOs.
Now - I don't see a reason why would my several Activities/classes need more than just a single instance of DAO. What's more, why would a DAO need an instance of DBHelper just for itself? I'm pretty sure they can share, especially I don't predict a situation when both DAOs would want to perform some operation on my database at the same time. So let's see some classes:

  • DBHelper

    @Singleton
    public class DBHelper extends SQLiteOpenHelper {
          //some not relevant configuration stuff
          private Context context;
    
          @Inject
          public DBHelper(Context context) {
               super(context, DATABASE_NAME, null, DATABASE_VERSION);
               this.context = context;
          }
    
         @Override
         public void onCreate(SQLiteDatabase db) {
              //db.execSQL, creating tables and stuff
         }
    
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
              //dropping tables, creating a new one
              onCreate(db);
         }
    }
    
  • example of DAO

        public interface SomeDataDAO {
            void hai();
        }
    
        public class SomeDataDAOImpl implements SomeDataDAO {
             private DBHelper dbHelper;
    
             public SomeDataDAOImpl(DBHelper dbHelper){
                   this.dbHelper = dbHelper;
             }
             @Override
             public void hai() {
                  SQLiteDatabase database = dbHelper.getWritableDatabase();
                  dbHelper.doSomeDatabaseStuff()
             }
        }
    
  • SomeDataModule

    @Module(
            injects = { MainActivity.class, SomeServiceImpl.class }
            )
    public class SomeDataModule {
          private Context appContext;
    
          @Provides @Singleton
          public SomeDataDAO provideSomeDataDao(DBHelper dbHelper){
                return new SomeDataDAOImpl(dbHelper);
          }
    
          @Provides @Singleton
          public ISomeService provideSomeService(SomeServiceImpl impl){
                 return impl;
          }
    
          @Provides
          public Context getAppContext(){
                 return this.appContext;
          }
          public SomeDataModule(){
                 this.appContext = MainActivity.getAppContext();
          }
    }
    
  • Now let's see two examples of dependency consumers

    public class MainActivity extends ActionBarActivity {
        @Inject
        SomeDataDAO someDataDAO;
        private ObjectGraph objectGraph;
        @Inject
        ISomeService someService;
        private static Context appContext;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             appContext = getApplicationContext();
             objectGraph = ObjectGraph.create(SomeDataModule.class);
             objectGraph.inject(this);
             someService.doSomeStuff();
        }
    
       public static Context getAppContext() {
             return appContext;
       }
    }
    


    public class SomeServiceImpl implements ISomeService {
        private ObjectGraph objectGraph;
        @Inject public SomeDataDAO someDataDAO;
    
        public SomeServiceImpl(){
            objectGraph = ObjectGraph.create(GraphDataModule.class);
            objectGraph.inject(this);
        }
    
        public void doSomeStuff(){
             someDataDAO.hai();
        }
    
    }
    


It works, however when I inject(this) twice, Dagger obviously creates me two instances of DBHelper as it thinks "One Singleton fon one graph instance". How do I wrap my SomeDataModule in ApplicationModule, so I have only one instance of DBHelper in the entire app? Adding @Singleton to DBHelper unfortunately was not enough.

Community
  • 1
  • 1
spoko
  • 783
  • 1
  • 10
  • 24
  • If you have to go singleton, why not use a Global class with static members and methods? – Kristy Welsh Dec 02 '14 at 19:32
  • @KristyWelsh I'm learning Android & Dagger. Even if in this particular case class with static members and methods could be better, I'd like to know how to achieve my goal with dependency injection, especially when I have quite easy example right now. By the way, is it even a proper design pattern? I don't think I have ever came across such approach. I met class with static fields giving constants, SQLs and so on, but never distributing DAOs. – spoko Dec 02 '14 at 20:43

1 Answers1

3

The easy, short and the answer which solves a minor design flaw and with that your problem is the following. Instead of creating a new ObjectGraph to inject the SomeDataDAO instance in your SomeServiceImpl, you really should pass the SomeDataDAO to your constructor. That is actually what dependency injection typically means: use the constructor of some object to inject its dependencies:

public class SomeServiceImpl implements ISomeService {

    private final SomeDataDAO someDataDAO;

    @Inject
    public SomeServiceImpl(SomeDataDAO someDataDAO){
        this.someDataDAO = someDataDAO;
    }

    public void doSomeStuff(){
         someDataDAO.hai();
    }
}

Now this is where the magic happens. Notice the @Inject annotation for the constructor? It tells Dagger to use this constructor whenever an instance of SomeServiceImpl is requested, and queries the ObjectGraph for its parameters.

So when you've got this Provides method below, Dagger already knows that SomeServiceImpl depends on SomeDataDAO, and uses your provideSomeDataDAO method to provide that instance. And, hey, it's a singleton, so this is the exact same instance as any other instance of SomeDataDAO retrieved by this ObjectGraph!

  @Provides @Singleton
  public ISomeService provideSomeService(SomeServiceImpl impl){
         return impl;
  }

In fact, you don't want to use ObjectGraph.inject(...) too often, you actually want to use the above methods. There are cases however, where you don't decide what the constructor looks like. In Android for example, these are Activity and View classes, amongst others. For these special cases, ObjectGraph.inject was created, so you can still inject your dependencies using Dagger.

Final note: In SomeServiceImpl I have made the someDataDAO private. As you probably know, this is the preferred way to handle instance fields, and using injectable constructors enables this.

Turns out this answer wasn't that short at all :O)

spoko
  • 783
  • 1
  • 10
  • 24
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • [This](https://github.com/square/dagger/tree/master/examples/android-activity-graphs) is an example of how multiple modules can be used by the way. – nhaarman Dec 02 '14 at 21:44