Those who are free of resentful thoughts surely find peace. - Buddha
Posted on 29th Oct 2017
Part 2 we implemented the DataManager
, DbHelper
, SharedPrefsHelper
and we modeled the class so that we can retrieve the value from the database.
In next steps we will learn how we can create a Component & how it wire itself in the DemoApplication to generate the dependent object.
Create DemoApplication
class that extends android.app.Application
public class DemoApplication extends Application {
protected ApplicationComponent applicationComponent;
@Inject
DataManager dataManager;
public static DemoApplication get(Context context) {
return (DemoApplication) context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
}
public ApplicationComponent getComponent(){
return applicationComponent;
}
}
Add this class inAndroidManifest.xml
<application
...
android:name=".DemoApplication"
...
application>
Notes: This class gets DataManager through DI. The interesting part in this class is the ApplicationComponent . Let’s go on with the steps before we cover this point.
Step 8:
Create class MainActivity
public class MainActivity extends AppCompatActivity {
@Inject
DataManager mDataManager;
private ActivityComponent activityComponent;
private TextView mTvUserInfo;
private TextView mTvAccessToken;
public ActivityComponent getActivityComponent() {
if (activityComponent == null) {
activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(DemoApplication.get(this).getComponent())
.build();
}
return activityComponent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getActivityComponent().inject(this);
mTvUserInfo = (TextView) findViewById(R.id.tv_user_info);
mTvAccessToken = (TextView) findViewById(R.id.tv_access_token);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
createUser();
getUser();
mDataManager.saveAccessToken("ASDR12443JFDJF43543J543H3K543");
String token = mDataManager.getAccessToken();
if(token != null){
mTvAccessToken.setText(token);
}
}
private void createUser(){
try {
mDataManager.createUser(new User("Ali", "1367, Gurgaon, Haryana, India"));
}catch (Exception e){e.printStackTrace();}
}
private void getUser(){
try {
User user = mDataManager.getUser(1L);
mTvUserInfo.setText(user.toString());
}catch (Exception e){e.printStackTrace();}
}
}
We have to provide the dependencies expressed in the DemoApplication
class. This class needs DataManager
and to provide this class we have to provide the dependencies expressed by DataManager
, which as mentioned in the constructor are Context
, DbHelper
, and SharedPrefsHelper
. We then move further in the graph.
Also create activity_main.xml
<xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_user_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:padding="10dp"/>
<TextView
android:id="@+id/tv_access_token"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:padding="10dp"/>
To provide the dependency for a class we have to create a Module class. This class defines the methods that provide the dependency. A Module class is identified by @Module
and the dependency provider method in identified by @Provides
.Now let’s hold on for some time and review the Dagger2.
@Inject
and a class that provides the dependency i.e. annotated with @Module
.Let’s move back to the example steps to understand the above statements.
Context
has to be ApplicationContext
DbHelper
needs Context
, dbName
, and version
. This does not have any further branching.SharedPrefsHelper
needs SharedPreferences
We now accumulate the superset of all these dependencies, which turn out to be: Context
, dbName
, version
, and SharedPreferences
Now to provide these dependencies we create ApplicationModule
@Module
public class ApplicationModule {
private final Application mApplication;
public ApplicationModule(Application app) {
mApplication = app;
}
@Provides
@ApplicationContext
Context provideContext() {
return mApplication;
}
@Provides
Application provideApplication() {
return mApplication;
}
@Provides
@DatabaseInfo
String provideDatabaseName() {
return "demo-dagger.db";
}
@Provides
@DatabaseInfo
Integer provideDatabaseVersion() {
return 2;
}
@Provides
SharedPreferences provideSharedPrefs() {
return mApplication.getSharedPreferences("demo-prefs", Context.MODE_PRIVATE);
}
}
Note: We have annotated this class with @Module
and all the methods with @Provides
.
The @Module
annotation tells Dagger that the ApplicationModule
class will provide dependencies for a part of the application. It is normal to have multiple Dagger modules in a project, and it is typical for one of them to provide app-wide dependencies.
Let’s explore this class:
Application
instance. This instance is used to provide other dependencies. We’ve added a private field to hold a reference to the application
object, a constructor to configure application
, and a provideContext()
method that returns the application
object. Notice that there are one more Dagger annotations on that method: @Provides
.@Provides
annotation tells Dagger that the method provides a certain type of dependency, in this case, a Context
object. When a part of the app requests that Dagger inject a Context
, the @Provides
annotation tells Dagger where to find it.Components
Now that you have a Dagger module that contains a dependency that can be injected, how do you use it?
That requires the use of another Dagger annotation, @Component
. We create a Component which links the DemoApplication
dependency and the ApplicationModule
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(DemoApplication demoApplication);
@ApplicationContext
Context getContext();
Application getApplication();
DataManager getDataManager();
SharedPrefsHelper getPreferenceHelper();
DbHelper getDbHelper();
}
Notes: ApplicationComponent
is an interface that is implemented by Dagger2. Using @Component
we specify the class to be a Component. You’ve told Dagger that ApplicationComponent
is a singleton component interface for the app. The @Component
annotation takes a list of modules
as an input, and you’ve added ApplicationModule
to the list.
The component is used to connect objects to their dependencies, typically by use of overridden inject()
methods. It takes the place of the reflective object graph in the original version of Dagger. In order to use the component, it must be accessible from the parts of the app that need injection. Here we have written a method inject
where we pass the DemoApplication
instance. Why do we do it?
When the dependencies are provided through field injection i.e. @inject
on the member variables, we have to tell the Dagger to scan this class through the implementation of this interface. This class also provides methods that are used to access the dependencies that exist in the dependency graph.
Next part4 here we would learn how the dependenent object gets generated in DaggerApplicationComponent when we provide the ApplicationModule
class to it to construct the dependencies.
Good, better, best. Never let it rest. Untill your good is better and your better is best. - St. Jerome