Dependency inversion library for Android.
Dependency inversion principle helps to decouple high-level modules from the low-level module implementation details and completes SOLID object-oriented design. If you are about to modularize your Android application and apply dependency inversion princible, then Magnet library would be a good fit for your project as it makes dependency inversion a fun task.
Let's compare traditional layered design to the desing based on dependency inversion principle. As an example we take an application consisting of a navigation bar with three tabbed pages.
Dependency inversion principle requires us to desing our application for extension right away instead of adding feature-specific code here and there uncontrolled, by spreading it all over the application as the application grows. Needless to say how more extensible, testable and maintainable our application becomes when it is desined for extension.
Another advantage of structuring the application in such a modular way is the ability to repackage the app according to new requirements. For instance we want to create a new companion app which uses some of the already existing modules or we want to create an Android Instant app. If we did everything right, we will be able to repackage some of existing modules into the new app and write missing ones.
As the name of the principle says we need to invert dependencies. If we simply invert dependencies then our library modules will depend on the application module. At the same time application module has to dependend on the library modules because it has to include them into the apk. We run into a circular dependencies situation which is not allowed in gradle builds. To resolve this circular dependency we have to avoid any dependency onto the application module. It means none of the library modules may depend on the application module. This would be only possible if our application module would have no code or resources the other modules need. This brings us to the design depicted below.
Application module becomes nearly empty and has the single role - it assembles all modules together into a single apk. Any features our application has, have to be moved to the library modules.
Our sample application defines app-main
library module which can host tabbed pages. We make it extensible by exposing Page
interface to its extension. Then we create thee more library modules which extend app-main
module by providing HomePage
, DashboardPage
and NotificationsPage
implementations of Page
interface.
Now we can ask Magnet to put all these pieces together.
- We add
@Implementation
annotation to the implementations ofPage
interface. This will allow Magnet to find those implementations at build-time. - In
MainActivity.onCreate()
method inapp-main
module we ask Magnet to instantiate all available implementations of thePage
interface at runtime. - Last but not least, we create an empty marker interface inside the
app
module and annotate it with@MagnetizeImplementations
. This will force Magnet to collect and index all implementations registred in the app.
Sample application located in this repo implements exactly the logic described above. You can build, run and debug it. The fun part is, that now you can remove any of the implementation modules from the build by simply commenting out respective implementation
dependency inside the build.gradle
file of the sample-app
module. Just rerun the app and commented out page will disappear. Have fun and happy coding!
Kotlin:
dependencies {
api "de.halfbit:magnet-kotlin:0.0.6"
kapt "de.halfbit:magnet-processor:0.0.6"
}
Java:
dependencies {
api 'de.halfbit:magnet:0.0.6'
annotationProcessor 'de.halfbit:magnet-processor:0.0.6'
}
Copyright 2018 Sergej Shafarenka, www.halfbit.de
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.