RESUME for Dagger2 and MVI

  1. prepare dagger for application and activity
  2. define a contract which includes interactor and view
  3. define needed functions in these interfaces
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
/**
* mainActivity contract
*/
public interface MainActivityContract {

/**
* interactor for mainActivity
*/
interface Interactor {
/**
* attach view to mainActivity
*
* @param view
*/
void onAttachView(View view);

/**
* detach view form mainActivity
*/
void onDetachView();

/**
* user login
*
* @param userName
* @param password
*/
void login(String userName, String password);
}

/**
* view for mainActivity
*/
interface View {
/**
* execute if login success
*/
void loginSuccess();

/**
* execute if login fail
*/
void loginFail();
}
}
  1. create a new class to implement interactor, override functions
  2. implement view in MainActivity; override functions
  3. add interactor dependecy for MainActivity by adding @Inject
1
2
@Inject
MainActivityContract.Interactor interactor;
  1. implement interactor injector by creating a interactor module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* interactor module
*/
@Module
public class InteractorModule {

/**
* provide mainActivity's interactor implementation
* @param authManager get instance from authManagerModule injection
* @return return instance implementation
*/
@Provides
public MainActivityContract.Interactor getMainActivityContract(){
return new MainActivityInteractorImpl();
}
}
  1. associate this module and dependent class here is MainActivity by adding this module to a component
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
/**
* application dagger component
*/
@Singleton
@Component(modules = {
AndroidInjectionModule.class
, AppWidgetModule.class
, ApplicationModule.class
, InteractorModule.class
, AuthManagerModule.class
})
public interface AppComponent {

/**
* provide custom builder for dagger component
*/
@Component.Builder
interface Builder {

/**
* provide app context
*
* @param authApplication auth application
* @return instance
*/
@BindsInstance
Builder application(AuthApplication authApplication);

/**
* builder
*
* @return app component
*/
AppComponent build();
}

/**
* method to inject app
*
* @param authApplication auth app instance
*/
void inject(AuthApplication authApplication);
}
  1. implement detail functions in interactor and mainActivity(view)
  2. if during step 8 we need some ohter modules/libraries ex, here we need a authManager to get token from backend,
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
/**
* mainActivity interactor implementation
*/
public class MainActivityInteractorImpl implements MainActivityContract.Interactor {

@Nullable
MainActivityContract.View view;

@NonNull
final AuthManager authManager;

@Override
public void onAttachView(MainActivityContract.View view) {
this.view = view;
}

@Override
public void onDetachView() {
this.view = null;
}

@Override
public void login(String userName, String password) {
authManager.login(userName, password, EnumSet.noneOf(AuthScope.class), new Listener() {
@Override
public void onSuccess() {
view.loginSuccess();
}

@Override
public boolean onFailure(@NonNull RequestError error, boolean alreadyHandled) {
view.loginFail();
return false;
}
});
}
}
  1. we can encapsulate its functions in a dagger module don’t forget add it to component to associate it with Interactor class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* authManager module
*/
@Module()
public class AuthManagerModule {

/**
* provide authManager implementation
* @param applicationContext get instance from ApplicationModule injection
* @return instance
*/
@Provides
AuthManager getAuthManagerProvider(Context applicationContext){
return new DebugAuthManagerProvider(applicationContext).get();
}
}
  1. but we still can’t get its instance cause it hasn’t been associated with interactor module. We need to add authManager in Paramater
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* interactor module
*/
@Module
public class InteractorModule {

/**
* provide mainActivity's interactor implementation
* @param authManager get instance from authManagerModule injection
* @return return instance implementation
*/
@Provides
public MainActivityContract.Interactor getMainActivityContract(AuthManager authManager){
return new MainActivityInteractorImpl(authManager);
}
}
  1. Then create a new interactor construction function which takes authManager as parameter
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
/**
* mainActivity interactor implementation
*/
public class MainActivityInteractorImpl implements MainActivityContract.Interactor {

@Nullable
MainActivityContract.View view;

@NonNull
final AuthManager authManager;

/**
* constructor
*
* @param authManager used to get token
*/
public MainActivityInteractorImpl(AuthManager authManager) {
this.authManager = authManager;
}

@Override
public void onAttachView(MainActivityContract.View view) {
this.view = view;
}

@Override
public void onDetachView() {
this.view = null;
}

@Override
public void login(String userName, String password) {
authManager.login(userName, password, EnumSet.noneOf(AuthScope.class), new Listener() {
@Override
public void onSuccess() {
view.loginSuccess();
}

@Override
public boolean onFailure(@NonNull RequestError error, boolean alreadyHandled) {
view.loginFail();
return false;
}
});
}
}

Important Thing

Dagger Inject for android component(ex. activity, fragment, application etc ) and for java class(or interface) is different. Cause for android, with FSM activity’s construction function is implicit. So we use @inject or @module and some dagger 2 Android annotation to realise injection. It seems simple and easy. But for other our custom classes, we need to do in common ways, like add dependencies as parameters in construction functions.

GO BACK TO Dagger

The simplest concept:

First we need to know the importance and usage of DI and Constructor Injection. You pass the dependencies of a class to its constructor.

Here explains why we need another way for injection.

  • Constructor Injection. This is the way described above. You pass the dependencies of a class to its constructor.
  • Field Injection (or Setter Injection). Certain Android framework classes such as activities and fragments are instantiated by the system, so constructor injection is not possible. With field injection, dependencies are instantiated after the class is created. The code would look like this:

Why we need auto Injection framework?

  • For big apps, taking all the dependencies and connecting them correctly can require a large amount of boilerplate code. In a multi-layered architecture, in order to create an object for a top layer, you have to provide all the dependencies of the layers below it. As a concrete example, to build a real car you might need an engine, a transmission, a chassis, and other parts; and an engine in turn needs cylinders and spark plugs.
  • When you’re not able to construct dependencies before passing them in — for example when using lazy initializations or scoping objects to flows of your app — you need to write and maintain a custom container (or graph of dependencies) that manages the lifetimes of your dependencies in memory.

Another way to improve decoupling of classes

service locator

You create a class known as the service locator that creates and stores dependencies and then provides those dependencies on demand.

Compare

  • The collection of dependencies required by a service locator makes code harder to test because all the tests have to interact with the same global service locator.
  • Dependencies are encoded in the class implementation, not in the API surface. As a result, it’s harder to know what a class needs from the outside. As a result, changes to Car or the dependencies available in the service locator might result in runtime or test failures by causing references to fail.
  • Managing lifetimes of objects is more difficult if you want to scope to anything other than the lifetime of the entire app.