Modular Android Apps

Jarosław Michalik

Android Developer @ Sointeractive
Tech Mentor @ Jaksiemasz.care

Smart City app

Generic app for concerts, conferences, city events...

Offers, partners, localization, events

  • User profile
  • News
  • City events
  • City transport and navigation
  • Notifications
  • Sponsors and partners list
  • Gamification
  • Social media integration

Various dependencies between components

  • Read article about unknown place in the city
  • Navigate to specific place with speciall offer
  • Notify user about new offer from special partner

And much more...

One project - many cities

Multiple deployments

handle it with gradle flavors

                    
productFlavors {
    roma {
        applicationId "com.company.smartcity.roma"
        resValue("string", "app_name", "roma")
        versionCode 33
        versionName "$googlePlayVersion"
        manifestPlaceholders = [facebookId: "***************"]
    }
    praga {
        applicationId "com.company.smartcity.praga"
        resValue("string", "app_name", "praga")
        versionCode 20
        versionName "$googlePlayVersion"
        manifestPlaceholders = [facebookId: "***************"]
    }
    budapest {
        applicationId "com.company.smartcity.budapest"
        resValue("string", "app_name", "budapest")
        versionCode 8
        versionName "$googlePlayVersion"
        manifestPlaceholders = [facebookId: "***************"]
    }
}
                    
                

Each city has different

  • app ID
  • server urls
  • transport options
  • languages available
  • RSS (newsfeed)
  • colours

published separately to Google Play

It can be done with Gradle flavors

But...

What if one client doesn't want news?

How to handle different deployments with one codebase?

                    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            if (BuildConfigHelper.isRomaFlavor()) {
                 addNewsToDrawer()
            }
        }
    }
                    
                

Unused code in production!

New task - add city game

Gamification - that's what we do!

Create new app, or add city game to existing one?

Instead - create module

IDEA

make smart city app

great again

MODULAR

And great too!

Gamification module

                
    Gamification.Builder()
        .with(app)
        .httpClient(client) //endpoint and interceptors configured
        .database(dbName) //realm file name
        .notificationHandler(fcmHandler)
        .specialOffersHandler(offersHandler)
        .build();
                
            

Resources (strings, drawables, styles) are overridden in main app

Gamification module has it's own

  • logic
  • build.gradle
  • networking
  • database
  • views

Shared dependencies:

  • standard and support libraries
  • RxJava
  • Retrofit
  • Dagger

Module can be easily integrated into other app

do not enforce architecture

App can work flawless without module

create some layers of abstraction

Each module can become separate app

config should be simple as possible

So what is module?

something, that provides bossiness logic

something, that can be used many times in different projects

Creating module in Android Studio

settings.gradle

                    
    include ':app', ':gamification'
                    
                

app-level build.gradle

                    
    dependencies {
        (....)
        compile project(':gamification')
    }
                    
                

Is this your package?

One package per functionality

Almost good...

Perfect?

Not perfect, just a good start

cleaner architecture -> less painful modularization

Manage dependencies the good way

                
ext {

    def RxJava2Version = '2.0.6'
    def RxAndroid2Version = '2.0.1'
    def Retrofit2Version = '2.3.0'
    def OkHttp3Version = '3.8.0'

    rxJava2Dependencies = [
            rxJava   : "io.reactivex.rxjava2:rxjava:${RxJava2Version}",
            rxAndroid: "io.reactivex.rxjava2:rxandroid:${RxAndroid2Version}"
    ]

    retrofit2Dependencies = [
            retrofit             : "com.squareup.retrofit2:retrofit:${Retrofit2Version}",
            retrofitRxJavaAdapter: "com.squareup.retrofit2:adapter-rxjava2:${Retrofit2Version}",
            okhttp               : "com.squareup.okhttp3:okhttp:${OkHttp3Version}",
            okHttpLogging        : "com.squareup.okhttp3:logging-interceptor:${OkHttp3Version}",
            gsonConverter        : "com.squareup.retrofit2:converter-gson:${Retrofit2Version}"
    ]
}


                
            

top-level build.gradle

                    
apply from: 'dependencies.gradle'
                    
                

module-level build.gradle

                    
compile rxJava2Dependencies.values()
                    
                

Compile only things you need

                    
testCompile jUnitDependencies.values() //only test builds

compile supportDependencies.values() //compile for all

flavorOneCompile gameDependencies.values() //compile in flavor build
flavorOneCompile newsDependencies.values()

flavorTwoCompile newsDependencies.values()
                    
                

Gradle tips - use dependency only during compilation

                    
provided codeGenerationTools.values()
                    
                

Ultimate goal - share modules across projects

Share module between phone app, smartTV or Android Things!

Separate app for module

e.x app displaying news and news only

Separate git repo for module

Tests

Prepare mock backend for module

At Sointeractive we use RAML (Rest Api Modelling Language)

https://github.com/isaacloud/local-api

Simple mock backend - ready to use with module demo app

Module sharing

git clone from module repo...

or...

Use maven artifactory!

                    
dependencies{
    compile "com.yourcompany.modules:gamification:1.0.2"
    compile "com.yourcompany.modules:news:1.2.4"
}
                    
                
https://www.jfrog.com/open-source/

Next level modularization

Facebook...

LinkedIn...

New app for major functionality

But they actually can convince users to download another app

Why you should try modules?

Your app is too large

You provide similar features to different customers

You build app across the teams

You want to go open source someday

Code as a crime scene

https://codescene.io/

Tool to analyse code

You want to try some new technologies, patterns or architecture

Don't create module if...

you are 100% sure that was one-time project

you don't have to maintain app

you have to meet the deadline

Smart City before

One module (app), various "ifs", unused code in production

Average compilation time: 15min

Smart City now

  • news module
  • gamification module
  • tripplanner module
  • Sointeractive beacon library

Compilation time after modularization:

4min

Average apk size (for different cities)

Before:

90mb

Now

20mb

Thank you!

Jarosław Michalik

https://rozkminiacz.github.io/

michalik@protonmail.ch

Q&A

Why we still use Java 7 in Android?