This post shows how to create a sample Android app that uses Dagger2 and greenDAO ORM. The codes are also organized in service and DAO layers. Previously, we touched briefly on greenDAO ORM for SQLite. This time we’re using the Dependency Injection framework Dagger2. The framework will help to instantiate our classes and inject the objects accordingly.
Requisites
The requisites are similar to those in Android GreenDAO ORM for SQLite. However, this time we include Dagger2-related dependencies. In the end, we’ll have the following codebase structure.
There are two fragments and one activity. For brevity, our codes only retrieve the lists of Person and Pet entities. The MainActivity class is where we trigger the dependency injection, and it has a reference to our Dagger2 component object. On the other hand, the two fragments use the PersonService and PetService to retrieve entities, nothing else. If everything works, there shouldn’t be any Exceptions from these services.
Update Android build.gradle for Dagger2 and greenDAO ORM Dependencies
Modify the build.gradle file as shown below. Note the highlighted line. We included the line classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:4.1.2" classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } |
Next, we update the app/build.gradle file, as shown below. We added the four highlighted lines.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | plugins { id 'com.android.application' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" defaultConfig { applicationId "com.turreta.dagger2greendaoormexample" minSdkVersion 29 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } apply plugin: 'org.greenrobot.greendao' } dependencies { implementation 'com.google.dagger:dagger:2.33' annotationProcessor 'com.google.dagger:dagger-compiler:2.33' implementation 'org.greenrobot:greendao:3.3.0' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.navigation:navigation-fragment:2.3.4' implementation 'androidx.navigation:navigation-ui:2.3.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } |
Android greenDAO ORM Entities
Then, we create our greenDAO ORM entities. Before building the whole Android project, we have the following original Java codes. For the Person entity, we have these codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.turreta.dagger2greendaoormexample.entity; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.ToMany; import java.util.List; @Entity(indexes = { @Index(value = "firstName,lastName", unique = true) }) public class Person { @Id(autoincrement = true) private Long id; @NotNull private String firstName; @NotNull private String lastName; @ToMany(referencedJoinProperty = "personId") private List<Pet> petList; } |
For the Pet entity, we have the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.turreta.dagger2greendaoormexample.entity; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.NotNull; import org.greenrobot.greendao.annotation.ToOne; @Entity public class Pet { @Id(autoincrement = true) private Long id; @NotNull private String petName; @NotNull private Long personId; @ToOne(joinProperty = "personId") private Person person; } |
Then, build the project via the Build > Make Project menu item to generate the DAO and other ORM-related classes.
The Service Classes
Next, we can write our services. For the PetService class, we have the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package com.turreta.dagger2greendaoormexample.di.service; import com.turreta.dagger2greendaoormexample.entity.Pet; import com.turreta.dagger2greendaoormexample.entity.PetDao; import java.util.List; import javax.inject.Inject; public class PetService { private PetDao petDao; @Inject public PetService(PetDao petDao) { this.petDao = petDao; } public List<Pet> getAllPets() { return this.petDao.queryBuilder().list(); } } |
Lastly, we have the codes for the PersonService class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.turreta.dagger2greendaoormexample.di.service; import com.turreta.dagger2greendaoormexample.entity.Person; import com.turreta.dagger2greendaoormexample.entity.PersonDao; import java.util.List; import javax.inject.Inject; public class PersonService { private PersonDao personDao; @Inject public PersonService(PersonDao personDao) { this.personDao = personDao; } public List<Person> getAllPersons() { return this.personDao.queryBuilder().list(); } } |
Android Dagger2 Component and Modules
Our codes have a Dagger2 component and two modules. The TurretaComponent interface has the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.turreta.dagger2greendaoormexample.di; import com.turreta.dagger2greendaoormexample.FirstFragment; import com.turreta.dagger2greendaoormexample.MainActivity; import com.turreta.dagger2greendaoormexample.SecondFragment; import com.turreta.dagger2greendaoormexample.di.service.PersonService; import com.turreta.dagger2greendaoormexample.di.service.PetService; import javax.inject.Singleton; import dagger.Component; @Singleton @Component(modules = {DbModule.class, AppModule.class}) public interface TurretaComponent { PersonService getPersonService(); PetService getPetService(); void inject(MainActivity mainActivity); void inject(FirstFragment firstFragment); void inject(SecondFragment secondFragment); } |
For simplicity, we have three inject methods explicitly for MainActivity, FirstFragment, and SecondFragment classes. The classes call these methods in their onCreate methods. Meanwhile, there are two modules in this class. The DBModule holds a reference to an application context and has the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package com.turreta.dagger2greendaoormexample.di; import android.content.Context; import com.turreta.dagger2greendaoormexample.entity.DaoMaster; import com.turreta.dagger2greendaoormexample.entity.DaoSession; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.query.QueryBuilder; import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @Module public class DbModule { private final Context context; public DbModule(Context context) { this.context = context; } @Provides @Singleton @Named("globalDaoSession") public DaoSession createDaoSession() { // regular SQLite database DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, "turreta-db"); Database db = helper.getWritableDb(); QueryBuilder.LOG_SQL = true; QueryBuilder.LOG_VALUES = true; return new DaoMaster(db).newSession(); } @Provides @Singleton public Context getContext() { return context; } } |
Unlike in the Android greenDAO ORM For SQLite Example, the new codes open or create the database when Dagger2 tries to instantiate a DaoSession class. Note that Dagger2 generates the DaoSession class when we build our project via the Build > Make Project menu option. Therefore, the class may not be readily available while we code.
The other module, the AppModule, uses the DBModule to reference the DaoSession object to create the services.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.turreta.dagger2greendaoormexample.di; import com.turreta.dagger2greendaoormexample.di.service.PersonService; import com.turreta.dagger2greendaoormexample.di.service.PetService; import com.turreta.dagger2greendaoormexample.entity.DaoSession; import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @Module(includes = DbModule.class) public class AppModule { @Provides @Singleton public static PersonService createPersonService(@Named("globalDaoSession") DaoSession daoSession) { return new PersonService(daoSession.getPersonDao()); } @Provides @Singleton public static PetService createPetService(@Named("globalDaoSession") DaoSession daoSession) { return new PetService(daoSession.getPetDao()); } } |
Try building the whole project to check everything is okay so far before moving on.
Modify Activity and Fragments To Use Dagger2
In this section, we modify the activity and the two fragments as little as possible. The MainActivity class will be the triggering point where the Dagger2 dependency injection to kickstart.
First, we add the following codes. The getter method allows our fragments to get a reference to our Dagger2 component via the getActivity method, as shown later.
1 2 3 4 5 6 7 | ... private TurretaComponent appComponent; public TurretaComponent getAppComponent() { return appComponent; } ... |
Second, modify the onCreate method to include these codes.
1 2 3 4 5 6 | ... // Bottom part of the method appComponent = DaggerTurretaComponent.builder().dbModule(new DbModule(this)) .build(); appComponent.inject(this); ... |
Now, we go on to the FirstFragment class. Add the following codes.
1 2 3 4 5 6 | ... private static final String TAG = "FirstFragment"; @Inject PersonService personService; ... |
Then, inject the Fragment object into the Dagger2 context, and try retrieving a list of Person entities in the onCreateView method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ... @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { ((MainActivity) getActivity()).getAppComponent().inject(this); List<Person> allPersons = this.personService.getAllPersons(); Log.d(TAG, "onCreateView: list size is " + allPersons.size()); // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_first, container, false); } ... |
Next, we do something similar to the SecondFragment class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ... private static final String TAG = "SecondFragment"; @Inject PetService petService; @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { ((MainActivity) getActivity()).getAppComponent().inject(this); List<Pet> allPets = this.petService.getAllPets(); Log.d(TAG, "onCreateView: list size is " + allPets.size()); // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_second, container, false); } ... |
Testing Our Android App
When we start the Android app and switch between the fragments by clicking the lone button, we can see codes write logs on the console. Along with the log comes the SQL SELECT queries used to try to retrieve records.
That’s it!