This is a proposal issue for introducing DI container (Dependency Injection)
For diverse opinions, please feel free to leave any comment, @amiq11.
TL;DR
- I hate the current structure of
...ActivityBase
and Flavor...Activity
- I want to restructure them by splitting
...ActivityBase
into common logic and flavor-specific logic
- I want to introduce Dagger2
Background
Currently we are going to implement product flavors.
There are two main features of product flavors: determining java files and merging xml.
(You can skip below 2 sections if you know product flavors)
determining java files
With product flavors, you can change files (or classes) by placing corresponding directories;
suppose you define 2 flavors (flavor1
, flavor2
) and have flavor1/java/com/example/Test.java
, and flavor2/java/com/example/Test.java
,
and the code in main/java/com/example/Main.java
refers to Test
class,
then the Test
class is referred with accordance of the product flavor in build-time.
merging xml
You can define AndroidManifest.xml
files for each product flavor and one main
.
main
does not belong to flavors, but it defines common resources (xml, java, images).
If you have AndroidManifest.xml
in main
and flavor1
, Android Studio merges the two xml files in build-time.
Why this is useful is that you can change resources according to the flavor.
A launch activity is defined in the manifest xml file. So you can choose the launch activity
by defining the manifest xml in the flavor.
Why we use it and How we used it
Why this feature is needed for us is because:
- we have to support multiple types of robots, each of which needs slightly different code (e.g., types of block, types of communication methods)
- but code-duplication has to be minimized
That's why we are going to use productFlavors.
We have nxt
, ev3
, and pile
robots so we'll have 3 product flavors.
In our current code, we we define TitleActivity
for each flavor and specify launch activities in manifest xml files for each flavor.
With the xml files, the launch activity of each app can start properly.
And for programming (block-manipulation) and execution (communication with robots) activities,
we define ProgrammingActivityBase
and ExecutionActivityBase
respectively,
and then in each flavor these classes are inherited according to the flavor (e.g., for nxt
, NxtProgrammingActivity
and NxtExecutioinActivity
).
The code of ProgrammingActivityBase
is like below:
public abstract class ProgrammingActivityBase extends Activity {
/* these methods are implemented in concrete classes */
protected abstract Intent getIntentForBlockList();
protected abstract Intent getIntentForExecution();
/* the rest is common for all flavors */
private void goToExecution() {
Intent intent = getIntetnForExecution();
...
}
...
};
Then in NxtProgrammingActivity
is like below:
public class NxtProgrammingActivity extends ProgrammingActivityBase {
@Override
protected Intent getIntentForBlockList() {
return new NxtBlockIntent();
}
@Override
protected Intent getIntentForExecution() {
return new NxtExecutionIntent();
}
}
Problem Statement
What I am concering is that the structure seems awkward and the base classes are too huge for abstract class.
In my opinion, the straightforward way for this situation is that defining interface for flavor-specific logic (intent-provider for programming)
and injecting flavor-specific-implementation into the interface.
So what I want to suggest is like below:
public class ProgrammingActivity extends Activity {
private IntentProvider intentProvider;
private void goToExecution() {
Intent intent = intentProvider.forExecution();
...
}
}
public interface IntentProvider {
Intent forExecution();
}
public class NxtIntentProvider {
@Override
public Intent forExecution() {
return ...
}
}
By splitting code like above, the code gets
- better organized
- easier to be mocked
- easier for new-comers to understand what is needed to be implemented for other flavors.
And this proposal also solves #31.
Coping with dependency by DI container
If you feel my proposal sounds good, you might wonder how the interfaces are injected
.
That kind of problems are called Dependency Injection.
There are a number of pages describing this problem, so please google it.
For Android projects, there is a cool DI container called Dagger2.
So I want to use this for our android project.
I tested the feasibility of my proposal in here
By using Dagger2, the code would be like below:
public class ProgrammingActivity extends Activity {
@Inject
IntentProvider intentProvider;
/* same as above */
}
How cool, isn't it?
Thank you for reading this too long issue.