Slide 1

Slide 1 text

MODULARIZING ANDROID APPLICATIONS Gérard Paligot Othman Barhdadi

Slide 2

Slide 2 text

Othman Barhdadi Technical Leader Plus @Modis Gérard Paligot Technical Leader Android @Decathlon

Slide 3

Slide 3 text

PRESENTATION Plus

Slide 4

Slide 4 text

APPLICATION ANDROID MONOLITHIQUE

Slide 5

Slide 5 text

APPLICATION ANDROID MONOLITHIQUE • Développement du deeplink • Maintenance difficile des applications inter-connectées • Durée de vie limitée des applications inter-connectées • Investissement à courte durée des marques Multiplication des applications

Slide 6

Slide 6 text

APPLICATION ANDROID MONOLITHIQUE Application Plus

Slide 7

Slide 7 text

APPLICATION ANDROID MONOLITHIQUE Application Plus avec les écrans de l’équipement du sportif

Slide 8

Slide 8 text

APPLICATION ANDROID MODULAIRE

Slide 9

Slide 9 text

DÉPENDANCES

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

IMPLEMENTATION VS API implementation noun Garde une dépendance locale au module déclaré et ne l’expose pas dans son api à travers le graphe de dépendance. api noun Permet l’utilisation de la dépendance à travers tout le graphe de dépendance du moment qu’elle est propagée.

Slide 12

Slide 12 text

OPTIMISATION ET OBFUSCATION

Slide 13

Slide 13 text

apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools defaultConfig { … } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles file('proguard-rules.pro') } } // … }

Slide 14

Slide 14 text

# No rules needed for now.

Slide 15

Slide 15 text

apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { compileSdkVersion versions.compileSdk defaultConfig { // … consumerProguardFiles 'consumer-proguard-rules.pro' } }

Slide 16

Slide 16 text

## Okio -dontwarn org.codehaus.mojo.animal_sniffer.* ## OkHttp -dontwarn javax.annotation.** -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase -dontwarn okhttp3.internal.platform.ConscryptPlatform ## Retrofit -keepattributes Signature, InnerClasses, EnclosingMethod -keepclassmembers,allowshrinking,allowobfuscation interface * { @retrofit2.http.* ; } -dontwarn kotlin.Unit -dontwarn retrofit2.-KotlinExtensions ## Kotlin -keep class kotlin.Metadata { *; } -keepclassmembers class kotlin.Metadata { public ; }

Slide 17

Slide 17 text

MODULE PAR L’EXEMPLE

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

CRÉATION D’UN MODULE

Slide 20

Slide 20 text

class SportEquipmentApplication

Slide 21

Slide 21 text

class SportEquipmentApplication : HasSupportFragmentInjector

Slide 22

Slide 22 text

class SportEquipmentApplication : HasSupportFragmentInjector { @Inject lateinit var injector: DispatchingAndroidInjector fun inject(fragment: Fragment) { supportFragmentInjector().inject(fragment) } override fun supportFragmentInjector(): AndroidInjector = injector }

Slide 23

Slide 23 text

class SportEquipmentApplication : HasSupportFragmentInjector { // … fun onCreate(context: Context) { initApp(context).let { this.app = it app.inject(this) } } private fun initApp(context: Context): AppComponent = DaggerAppComponent.builder() .application(context.applicationContext as Application) .context(context) .build() // … }

Slide 24

Slide 24 text

package extensions import android.content.Context import androidx.fragment.app.Fragment import feature.SportEquipmentApplication private val appSingleton = SportEquipmentApplication() val Context.moduleApplication: SportEquipmentApplication get() = appSingleton val Fragment.moduleApplication: SportEquipmentApplication get() = appSingleton

Slide 25

Slide 25 text

class SportEquipmentMainFragment : Fragment()

Slide 26

Slide 26 text

class SportEquipmentMainFragment : Fragment() { override fun onAttach(context: Context) { super.onAttach(context) moduleApplication.onCreate(context) } }

Slide 27

Slide 27 text

class SportEquipmentMainFragment : Fragment() { override fun onAttach(context: Context) { super.onAttach(context) moduleApplication.onCreate(context) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) inject(context!!.moduleApplication) } override fun inject(panoplyApplication: SportEquipmentApplication) = panoplyApplication.inject(this) }

Slide 28

Slide 28 text

NAVIGATION INTERNE D’UN MODULE

Slide 29

Slide 29 text

interface ScreenSaverContracts

Slide 30

Slide 30 text

interface ScreenSaverContracts { interface View { fun show() } interface Presenter { fun load() } interface NavigationListener { fun onScreenTouched() } }

Slide 31

Slide 31 text

interface SportPageContracts { interface View { fun show() } interface Presenter { fun load() } interface NavigationListener { fun onProductSelected(id) } }

Slide 32

Slide 32 text

class SportEquipmentMainFragment : Fragment(), SportPageContracts.NavigationListener, ScreenSaverContracts.NavigationListener { // … }

Slide 33

Slide 33 text

class SportEquipmentMainFragment : Fragment(), SportPageContracts.NavigationListener, ScreenSaverContracts.NavigationListener { // … override fun onScreenTouched() { // navigation inside current module show(SportPageFragment(), addToBackStack = true) } }

Slide 34

Slide 34 text

class SportEquipmentMainFragment : Fragment(), SportPageContracts.NavigationListener, ScreenSaverContracts.NavigationListener { // … override fun onScreenTouched() { // navigation inside current module show(SportPageFragment(), addToBackStack = true) } override fun onProductSelected(id) { // navigation outside current module } }

Slide 35

Slide 35 text

NAVIGATION ENTRE MODULES

Slide 36

Slide 36 text

interface MainComContracts

Slide 37

Slide 37 text

interface MainComContracts { interface Module }

Slide 38

Slide 38 text

interface MainComContracts { interface Module { fun onBackPressed(): Boolean = false fun onUserInteraction() {} fun onRefreshModule() {} } }

Slide 39

Slide 39 text

class MainActivity : AppCompatActivity()

Slide 40

Slide 40 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module }

Slide 41

Slide 41 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) { replaceFragment(SportEquipmentMainFragment()) } else { currentFragment = supportFragmentManager .findFragmentById(R.id.container) as? MainComContracts.Module ?: throw IllegalStateException("No fragment found!") } } }

Slide 42

Slide 42 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) { replaceFragment(SportEquipmentMainFragment()) } else { currentFragment = supportFragmentManager .findFragmentById(R.id.container) as? MainComContracts.Module ?: throw IllegalStateException("No fragment found!") } } private fun replaceFragment(fragment: T) where T : Fragment, T : MainComContracts.Module { supportFragmentManager.inTransaction { currentFragment = fragment replace(R.id.container, fragment) } } }

Slide 43

Slide 43 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module }

Slide 44

Slide 44 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module override fun onBackPressed() { if (!currentFragment.onBackPressed()) { super.onBackPressed() } } }

Slide 45

Slide 45 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module override fun onBackPressed() { if (!currentFragment.onBackPressed()) { super.onBackPressed() } } override fun onUserInteraction() { super.onUserInteraction() currentFragment.onUserInteraction() } }

Slide 46

Slide 46 text

class MainActivity : AppCompatActivity() { private lateinit var currentFragment: MainComContracts.Module override fun onBackPressed() { if (!currentFragment.onBackPressed()) { super.onBackPressed() } } override fun onUserInteraction() { super.onUserInteraction() currentFragment.onUserInteraction() } fun onTimeoutOccurred() { currentFragment.onRefreshModule() } }

Slide 47

Slide 47 text

interface MainComContracts { interface Module { … } }

Slide 48

Slide 48 text

interface MainComContracts { interface Data { fun getStoreId(): String fun getActualLanguageCode(): String fun getActualCountryCode(): String fun getDeviceId(): String fun getKioskModel(): String } interface Module { … } }

Slide 49

Slide 49 text

interface MainComContracts { interface Toolbar interface Data { fun getStoreId(): String fun getActualLanguageCode(): String fun getActualCountryCode(): String fun getDeviceId(): String fun getKioskModel(): String } interface Module { … } }

Slide 50

Slide 50 text

interface MainComContracts { interface Toolbar interface Navigation { fun onShowProduct(codeModel: String?) fun onAddProductToBasket(codeModel: String?) } interface Data { fun getStoreId(): String fun getActualLanguageCode(): String fun getActualCountryCode(): String fun getDeviceId(): String fun getKioskModel(): String } interface Module { … } }

Slide 51

Slide 51 text

class SportEquipmentMainFragment : Fragment(), SportPageContracts.NavigationListener, ScreenSaverContracts.NavigationListener { // … override fun onScreenTouched() { // navigation inside current module show(SportPageFragment(), addToBackStack = true) } override fun onProductSelected(id) { // navigation outside current module } }

Slide 52

Slide 52 text

class SportEquipmentMainFragment : Fragment(), SportPageContracts.NavigationListener, ScreenSaverContracts.NavigationListener { // … override fun onScreenTouched() { // navigation inside current module show(SportPageFragment(), addToBackStack = true) } override fun onProductSelected(id) { (fragment.activity as MainComContracts.Navigation).onShowProduct(id) } }

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

NOS OBJECTIFS

Slide 55

Slide 55 text

NOS OBJECTIFS • Team working

Slide 56

Slide 56 text

NOS OBJECTIFS • Team working • Découplage de code

Slide 57

Slide 57 text

NOS OBJECTIFS • Team working • Découplage de code • Contributions externes

Slide 58

Slide 58 text

NOS OBJECTIFS • Team working • Découplage de code • Contributions externes • Unification des applications

Slide 59

Slide 59 text

NOS OBJECTIFS • Team working • Découplage de code • Contributions externes • Unification des applications • Réutilisation de code

Slide 60

Slide 60 text

NOS OBJECTIFS • Team working • Découplage de code • Contributions externes • Unification des applications • Réutilisation de code • Diversité des technologies

Slide 61

Slide 61 text

DIFFICULTÉS

Slide 62

Slide 62 text

DIFFICULTÉS • Diversité des technologies

Slide 63

Slide 63 text

DIFFICULTÉS • Diversité des technologies • Redondance de code

Slide 64

Slide 64 text

DIFFICULTÉS • Diversité des technologies • Redondance de code • Identification des dépendances

Slide 65

Slide 65 text

DIFFICULTÉS • Diversité des technologies • Redondance de code • Identification des dépendances • Unification de la vision de l'approche modulaire

Slide 66

Slide 66 text

LET’S THINK ABOUT THE FUTURE…

Slide 67

Slide 67 text

THE NAVIGATION ARCHITECTURE COMPONENT

Slide 68

Slide 68 text

APP BUNDLE

Slide 69

Slide 69 text

MODULARIZING ANDROID APPLICATIONS Gérard Paligot Othman Barhdadi