Slide 1

Slide 1 text

Why we failed at modularizing “our app” An honest retrospective Droidcon Berlin 19

Slide 2

Slide 2 text

Marcos Holgado @Orbycius

Slide 3

Slide 3 text

“Common sense is not that common” @Orbycius Voltaire, Dictionnaire Philosophique (1764)

Slide 4

Slide 4 text

@Orbycius

Slide 5

Slide 5 text

@Orbycius

Slide 6

Slide 6 text

@Orbycius

Slide 7

Slide 7 text

@Orbycius

Slide 8

Slide 8 text

@Orbycius

Slide 9

Slide 9 text

@Orbycius

Slide 10

Slide 10 text

@Orbycius

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

How does it work? @Orbycius

Slide 13

Slide 13 text

productFlavors { uk { ... apply from: 'uk.gradle' } international { ... apply from: 'international.gradle' } germany { ... apply from: 'germany.gradle' } italia { ... apply from: 'italy.gradle' } } @Orbycius

Slide 14

Slide 14 text

productFlavors { uk { ... apply from: 'uk.gradle' } international { ... apply from: 'international.gradle' } germany { ... apply from: 'germany.gradle' } italia { ... apply from: 'italy.gradle' } } @Orbycius App

Slide 15

Slide 15 text

productFlavors { uk { ... apply from: 'uk.gradle' } international { ... apply from: 'international.gradle' } germany { ... apply from: 'germany.gradle' } italia { ... apply from: 'italy.gradle' } } @Orbycius App src

Slide 16

Slide 16 text

productFlavors { uk { ... apply from: 'uk.gradle' } international { ... apply from: 'international.gradle' } germany { ... apply from: 'germany.gradle' } italia { ... apply from: 'italy.gradle' } } @Orbycius App src main

Slide 17

Slide 17 text

productFlavors { uk { ... apply from: 'uk.gradle' } international { ... apply from: 'international.gradle' } germany { ... apply from: 'germany.gradle' } italia { ... apply from: 'italy.gradle' } } @Orbycius App src main germany uk italia international

Slide 18

Slide 18 text

@Orbycius App src main germany uk italia international

Slide 19

Slide 19 text

@Orbycius src main uk germany

Slide 20

Slide 20 text

@Orbycius src main uk germany Feature 1

Slide 21

Slide 21 text

@Orbycius src main uk germany Feature 1

Slide 22

Slide 22 text

@Orbycius src main uk germany Feature 1 Feature 2

Slide 23

Slide 23 text

@Orbycius src main uk germany Feature 1.1 Feature 2

Slide 24

Slide 24 text

@Orbycius src main uk germany Feature 1.1 Feature 2

Slide 25

Slide 25 text

@Orbycius src main uk germany Feature 1.1 Feature 2

Slide 26

Slide 26 text

@Orbycius Author: Marcos Holgado <…@sky.uk> Date: Bug fix for tab colours... again Tue Oct 17 09:36:44 2017 +0100

Slide 27

Slide 27 text

@Orbycius Author: Marcos Holgado <…@sky.uk> Date: Bug fix for tab colours... again Author: Marcos Holgado <…@sky.uk> Date: Fix tab colours again :( Tue Oct 17 09:36:44 2017 +0100 Mon Oct 23 16:10:08 2017 +0100

Slide 28

Slide 28 text

@Orbycius Author: Marcos Holgado <…@sky.uk> Date: Bug fix for tab colours... again Author: Marcos Holgado <…@sky.uk> Date: Fix tab colours again :( Tue Oct 17 09:36:44 2017 +0100 Mon Oct 23 16:10:08 2017 +0100

Slide 29

Slide 29 text

Long build times Change one break many Painful to work with It doesn’t scale … @Orbycius

Slide 30

Slide 30 text

Convincing people is hard @Orbycius

Slide 31

Slide 31 text

Convincing people out of your team is hard @Orbycius

Slide 32

Slide 32 text

“The Vision” @Orbycius

Slide 33

Slide 33 text

Showcase App @Orbycius

Slide 34

Slide 34 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 35

Slide 35 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 36

Slide 36 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 37

Slide 37 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 38

Slide 38 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 39

Slide 39 text

Feature 1 @Orbycius Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 40

Slide 40 text

What is a module? @Orbycius

Slide 41

Slide 41 text

@Orbycius Feature modules Core

Slide 42

Slide 42 text

@Orbycius Feature modules Core

Slide 43

Slide 43 text

@Orbycius

Slide 44

Slide 44 text

@Orbycius Inputs

Slide 45

Slide 45 text

@Orbycius Inputs Outputs

Slide 46

Slide 46 text

@Orbycius Inputs Outputs listeners()

Slide 47

Slide 47 text

Inputs Outputs listeners() App @Orbycius

Slide 48

Slide 48 text

Inputs Outputs listeners() App @Orbycius implements

Slide 49

Slide 49 text

What is a feature? @Orbycius

Slide 50

Slide 50 text

Inputs Outputs App @Orbycius Article List

Slide 51

Slide 51 text

Inputs Outputs ArticleListActivity App @Orbycius Article List

Slide 52

Slide 52 text

Inputs Outputs ArticleListActivity App @Orbycius Article List ArticleWebViewActivity?

Slide 53

Slide 53 text

Inputs Outputs App @Orbycius Article List onArticleClick() Article Reader Inputs Outputs

Slide 54

Slide 54 text

Inputs Outputs App @Orbycius implements Article List onArticleClick() Article Reader Inputs Outputs

Slide 55

Slide 55 text

Inputs Outputs App @Orbycius implements Article List onArticleClick() Article Reader Inputs Outputs

Slide 56

Slide 56 text

The first module @Orbycius

Slide 57

Slide 57 text

App @Orbycius

Slide 58

Slide 58 text

App @Orbycius Streaming

Slide 59

Slide 59 text

App @Orbycius Streaming

Slide 60

Slide 60 text

App @Orbycius Streaming Live TV

Slide 61

Slide 61 text

App @Orbycius Streaming Live TV

Slide 62

Slide 62 text

App @Orbycius Streaming Live TV

Slide 63

Slide 63 text

Extremely complicated feature @Orbycius

Slide 64

Slide 64 text

Extremely complicated feature Hard deadline @Orbycius

Slide 65

Slide 65 text

Extremely complicated feature Hard deadline Mainly done by one person @Orbycius

Slide 66

Slide 66 text

Extremely complicated feature Hard deadline Mainly done by one person Very bad 1st example @Orbycius

Slide 67

Slide 67 text

Start with the easiest @Orbycius

Slide 68

Slide 68 text

Splash Screen @Orbycius

Slide 69

Slide 69 text

Start with the most valuable @Orbycius

Slide 70

Slide 70 text

Integrate ASAP @Orbycius

Slide 71

Slide 71 text

@Orbycius

Slide 72

Slide 72 text

@Orbycius

Slide 73

Slide 73 text

Don’t compromise your modules @Orbycius

Slide 74

Slide 74 text

Dependency Injection @Orbycius

Slide 75

Slide 75 text

App @Orbycius Streaming Live TV Streaming player

Slide 76

Slide 76 text

App @Orbycius Streaming Live TV Core Streaming player

Slide 77

Slide 77 text

App @Orbycius Streaming Live TV Core Streaming player

Slide 78

Slide 78 text

App @Orbycius Streaming Live TV Core Streaming player

Slide 79

Slide 79 text

App @Orbycius Streaming Live TV Core Streaming player User

Slide 80

Slide 80 text

App @Orbycius Streaming Live TV Core Streaming player User User

Slide 81

Slide 81 text

App @Orbycius Streaming Live TV Core Streaming player User User

Slide 82

Slide 82 text

App @Orbycius Streaming Live TV Core Streaming player User User

Slide 83

Slide 83 text

Dagger to the rescue @Orbycius

Slide 84

Slide 84 text

Dagger to the rescue @Orbycius ?

Slide 85

Slide 85 text

Dagger vs Koin @Orbycius

Slide 86

Slide 86 text

If you don’t want to understand it, don’t use it @Orbycius

Slide 87

Slide 87 text

SODD @Orbycius

Slide 88

Slide 88 text

Stack Overflow Driven Development @Orbycius

Slide 89

Slide 89 text

@Orbycius @Provides public ForegroundManager provideForegroundManager(Context context) { useForegroundManager = new ForegroundManager((Application) context); if (!foregroundManager.compareAndSet(null, useForegroundManager)) { useForegroundManager = foregroundManager.get(); } } return useForegroundManager; } private final AtomicReference foregroundManager; ForegroundManager useForegroundManager = foregroundManager.get(); if (useForegroundManager == null) { } @Singleton

Slide 90

Slide 90 text

@Orbycius @Provides public ForegroundManager provideForegroundManager(Context context) { useForegroundManager = new ForegroundManager((Application) context); if (!foregroundManager.compareAndSet(null, useForegroundManager)) { useForegroundManager = foregroundManager.get(); } } return useForegroundManager; } private final AtomicReference foregroundManager; ForegroundManager useForegroundManager = foregroundManager.get(); if (useForegroundManager == null) { } @Singleton

Slide 91

Slide 91 text

@Orbycius @Provides public ForegroundManager provideForegroundManager(Context context) { useForegroundManager = new ForegroundManager((Application) context); if (!foregroundManager.compareAndSet(null, useForegroundManager)) { useForegroundManager = foregroundManager.get(); } } return useForegroundManager; } private final AtomicReference foregroundManager; ForegroundManager useForegroundManager = foregroundManager.get(); if (useForegroundManager == null) { } @Singleton

Slide 92

Slide 92 text

@Orbycius public class PlayerActivity extends AppCompatActivity { @Inject Utility utility; @Override protected void onCreate(Bundle savedInstanceState) { .getStreamingComponent() .addSubcomponent(new StreamingModule(this)) .inject(this); } [...] } } // Check that a sub-class hasn't just done DI for us! if (utility == null) { StreamingModuleMain.getStreamingModuleHelper()

Slide 93

Slide 93 text

@Orbycius public class PlayerActivity extends AppCompatActivity { @Inject Utility utility; @Override protected void onCreate(Bundle savedInstanceState) { .getStreamingComponent() .addSubcomponent(new StreamingModule(this)) .inject(this); } [...] } } // Check that a sub-class hasn't just done DI for us! if (utility == null) { StreamingModuleMain.getStreamingModuleHelper()

Slide 94

Slide 94 text

@Orbycius public class PlayerActivity extends AppCompatActivity { @Inject Utility utility; @Override protected void onCreate(Bundle savedInstanceState) { .getStreamingComponent() .addSubcomponent(new StreamingModule(this)) .inject(this); } [...] } } // Check that a sub-class hasn't just done DI for us! if (utility == null) { StreamingModuleMain.getStreamingModuleHelper()

Slide 95

Slide 95 text

@Orbycius public interface ModuleMain { void initialise(); void terminate(); BaseNavObjectRegistrar getNavObjectRegistrar(); } ModuleHelper getModuleHelper();

Slide 96

Slide 96 text

@Orbycius public interface ModuleMain { void initialise(); void terminate(); BaseNavObjectRegistrar getNavObjectRegistrar(); } ModuleHelper getModuleHelper();

Slide 97

Slide 97 text

@Orbycius public interface ModuleMain { void initialise(); void terminate(); BaseNavObjectRegistrar getNavObjectRegistrar(); } public interface ModuleHelper { void setCoreComponent(CoreComponent coreComponent); CoreComponent getCoreComponent(); } ModuleHelper getModuleHelper();

Slide 98

Slide 98 text

Subcomponents @Orbycius

Slide 99

Slide 99 text

App @Orbycius Streaming @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } @Subcomponent(modules = [StreamingModule::class]) interface StreamingSubcomponent { fun inject(activity: PlayerActivity) } streamingComponent: StreamingSubcomponent

Slide 100

Slide 100 text

App @Orbycius Streaming @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } @Subcomponent(modules = [StreamingModule::class]) interface StreamingSubcomponent { fun inject(activity: PlayerActivity) } streamingComponent: StreamingSubcomponent

Slide 101

Slide 101 text

App @Orbycius Streaming @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } @Subcomponent(modules = [StreamingModule::class]) interface StreamingSubcomponent { fun inject(activity: PlayerActivity) } streamingComponent: StreamingSubcomponent

Slide 102

Slide 102 text

App @Orbycius Streaming .plus(streamingSubcomponent) .inject(this) @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } streamingComponent: StreamingSubcomponent appComponent

Slide 103

Slide 103 text

App @Orbycius Streaming .plus(streamingSubcomponent) .inject(this) @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } streamingComponent: StreamingSubcomponent appComponent

Slide 104

Slide 104 text

App @Orbycius Streaming .plus(streamingSubcomponent) .inject(this) @Component(modules = [AppModule::class]) interface AppComponent { fun inject(mainActivity: MainActivity) fun plus( ) } streamingComponent: StreamingSubcomponent appComponent

Slide 105

Slide 105 text

Subcomponents @Orbycius

Slide 106

Slide 106 text

Subcomponents @Orbycius

Slide 107

Slide 107 text

@Orbycius @Component( modules = StreamingModule.class, dependencies = CoreComponent.class ) public interface StreamingComponent { }

Slide 108

Slide 108 text

App @Orbycius Streaming Core interface CoreComponentProvider { CoreComponent provideCoreComponent(); } interface AppComponentProvider { AppComponent provideAppComponent(); } interface StreamingComponentProvider { StreamingComponent provideStComponent(); }

Slide 109

Slide 109 text

App @Orbycius Streaming Core interface CoreComponentProvider { CoreComponent provideCoreComponent(); } interface AppComponentProvider { AppComponent provideAppComponent(); } interface StreamingComponentProvider { StreamingComponent provideStComponent(); } class SkySportsApplication extends Application implements CoreComponentProvider, AppComponentProvider { }

Slide 110

Slide 110 text

@Orbycius interface AppComponentProvider { AppComponent provideAppComponent(); } class SkySportsApplication extends Application implements CoreComponentProvider, AppComponentProvider { private CoreComponent coreComponent; private AppComponent appComponent; @Override public CoreComponent provideCoreComponent() { if (coreComponent == null) { coreComponent = DaggerCoreComponent.builder() .commonModule(new CommonModule(this)) .build(); } return commonComponent; } }

Slide 111

Slide 111 text

@Orbycius public class UKSportsApplication extends implements { private StreamingComponent streamingComponent; } SkySportsApplication StreamingComponentProvider .coreComponent(provideCoreComponent()) @Override public StreamingComponent provideStreamingComponent() { if (streamingComponent == null) { streamingComponent = DaggerStreamingComponent.builder() .streamingModule(new StreamingModule( .build(); } return streamingComponent; } provideAppComponent().getUser()

Slide 112

Slide 112 text

@Orbycius public class UKSportsApplication extends implements { private StreamingComponent streamingComponent; } SkySportsApplication StreamingComponentProvider .coreComponent(provideCoreComponent()) @Override public StreamingComponent provideStreamingComponent() { if (streamingComponent == null) { streamingComponent = DaggerStreamingComponent.builder() .streamingModule(new StreamingModule( .build(); } return streamingComponent; } provideAppComponent().getUser()

Slide 113

Slide 113 text

@Orbycius public class UKSportsApplication extends implements { private StreamingComponent streamingComponent; } SkySportsApplication StreamingComponentProvider .coreComponent(provideCoreComponent()) @Override public StreamingComponent provideStreamingComponent() { if (streamingComponent == null) { streamingComponent = DaggerStreamingComponent.builder() .streamingModule(new StreamingModule( .build(); } return streamingComponent; } provideAppComponent().getUser()

Slide 114

Slide 114 text

@Orbycius public class UKSportsApplication extends implements { private StreamingComponent streamingComponent; } SkySportsApplication StreamingComponentProvider .coreComponent(provideCoreComponent()) @Override public StreamingComponent provideStreamingComponent() { if (streamingComponent == null) { streamingComponent = DaggerStreamingComponent.builder() .streamingModule(new StreamingModule( .build(); } return streamingComponent; } provideAppComponent().getUser()

Slide 115

Slide 115 text

@Orbycius public class UKSportsApplication extends implements { private StreamingComponent streamingComponent; } SkySportsApplication StreamingComponentProvider .coreComponent(provideCoreComponent()) @Override public StreamingComponent provideStreamingComponent() { if (streamingComponent == null) { streamingComponent = DaggerStreamingComponent.builder() .streamingModule(new StreamingModule( .build(); } return streamingComponent; } provideAppComponent().getUser()

Slide 116

Slide 116 text

@Orbycius public class PlayerActivity extends AppCompatActivity { @Inject Utility utility; @Inject User user; @Override protected void onCreate(Bundle savedInstanceState) { StreamingInjectHelper.provideStreamingComponent( getApplicationContext() ).inject(this); } }

Slide 117

Slide 117 text

@Orbycius public class StreamingInjectHelper { public static StreamingComponent provideStreamingComponent(final Context context){ if (context instanceof StreamingComponentProvider) { return ((StreamingComponentProvider)context).provideStreamingComponent(); } else { throw new IllegalStateException("The context you have passed does not implement StreamingComponentProvider"); } } }

Slide 118

Slide 118 text

Understand what you do Share knowledge @Orbycius

Slide 119

Slide 119 text

@Orbycius KI S S

Slide 120

Slide 120 text

@Orbycius K I S S Keep It Simple Stupid

Slide 121

Slide 121 text

@Orbycius Too smart to fail?

Slide 122

Slide 122 text

@Orbycius If the team can’t understand it, you already failed

Slide 123

Slide 123 text

@Orbycius @Provides @IntoMap @IntKey(TableRow.CRICKET_ROW) SportsListViewHolderFactory provideCricketViewHolder() { return new CricketStandingViewHolderFactory(); } @Provides @IntoMap @IntKey(TableRow.RUGBY_ROW) SportsListViewHolderFactory provideRugbyViewHolder() { return new RugbyStandingViewHolderFactory(); } @Provides @IntoMap @IntKey(TableRow.F1_ROW) SportsListViewHolderFactory provideF1DriverViewHolder() { return new F1DriverStandingViewHolderFactory(); }

Slide 124

Slide 124 text

@Orbycius @Provides Map provideSportsListViewHolderFactoryMap() { Map map = new HashMap<>(); map.put(TableRow.F1_ROW, new F1DriverStandingViewHolderFactory()); map.put(TableRow.CRICKET_ROW, new CricketStandingViewHolderFactory()); map.put(TableRow.RUGBY_ROW, new RugbyStandingViewHolderFactory()); return map; }

Slide 125

Slide 125 text

@Orbycius public class Config implements Parcelable { /** * Config Sections */ private final Map sections; }

Slide 126

Slide 126 text

@Orbycius public class Config implements Parcelable { /** * Config Sections */ private final Map sections; } public interface ConfigSection extends Parcelable { /** * Name of the section to use as a key * * @return Name of the section to use for lookups */ String getSectionName(); }

Slide 127

Slide 127 text

@Orbycius public class ConfigDeserialiser implements JsonDeserializer { /** * Deserialiser for individual sections, keyed on the sections they support * * Note that this means each deserialiser may appear more than once! */ private final Map sectionDeserialisers; }

Slide 128

Slide 128 text

@Orbycius public class ConfigDeserialiser implements JsonDeserializer { /** * Deserialiser for individual sections, keyed on the sections they support * * Note that this means each deserialiser may appear more than once! */ private final Map sectionDeserialisers; } public interface ConfigSectionDeserialiser { Collection supportedParentKeys(); String sectionKey(); @NonNull T deserialiseSection(String key, JsonElement sectionSource, @Nullable T existingSection); T getDefaultSection(); }

Slide 129

Slide 129 text

@Orbycius public class ForcedUpgradeDeserialiser implements ConfigSectionDeserialiser { public static final String LATEST_VERSION = "lver"; public static final String MESSAGE_TITLE = "title"; public static final String MESSAGE_BODY = "message"; public static final String FORCE_UPGRADE = "shouldForce"; }

Slide 130

Slide 130 text

@Orbycius public class ForcedUpgradeDeserialiser implements ConfigSectionDeserialiser { public static final String LATEST_VERSION = "lver"; public static final String MESSAGE_TITLE = "title"; public static final String MESSAGE_BODY = "message"; public static final String FORCE_UPGRADE = "shouldForce"; } public class ForcedUpgrade implements ConfigSection { public static final String FORCED_UPGRADE = "shouldForce"; /** * Latest version code */ private int latestVersion; }

Slide 131

Slide 131 text

@Orbycius “Deleted a lot of classes around config because let's be honest, is just a json so why over complicating it?”

Slide 132

Slide 132 text

@Orbycius “Deleted a lot of classes around config because let's be honest, is just a json so why over complicating it?”

Slide 133

Slide 133 text

Core @Orbycius

Slide 134

Slide 134 text

Core @Orbycius do you need it?

Slide 135

Slide 135 text

App @Orbycius Streaming Core User User

Slide 136

Slide 136 text

App @Orbycius Streaming Core User User

Slide 137

Slide 137 text

App @Orbycius Streaming Core User About

Slide 138

Slide 138 text

App @Orbycius Streaming Core User About other_libraries

Slide 139

Slide 139 text

App @Orbycius Streaming Core About other_libraries User User

Slide 140

Slide 140 text

App @Orbycius Streaming Core User User

Slide 141

Slide 141 text

App @Orbycius Streaming Core User User Streaming

Slide 142

Slide 142 text

Treat each module* as a library @Orbycius *feature module

Slide 143

Slide 143 text

Treat each module* as a library… maybe @Orbycius

Slide 144

Slide 144 text

Testing @Orbycius

Slide 145

Slide 145 text

Showcase App @Orbycius

Slide 146

Slide 146 text

@Orbycius Feature 1 Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8

Slide 147

Slide 147 text

Showcase App @Orbycius

Slide 148

Slide 148 text

Showcase App @Orbycius

Slide 149

Slide 149 text

Sample Apps @Orbycius

Slide 150

Slide 150 text

@Orbycius Feature 1 Feature 2 Feature 3 Feature 4 Feature 5 Feature 6 Feature 7 Feature 8 Feature 9

Slide 151

Slide 151 text

@Orbycius Resource conflicts

Slide 152

Slide 152 text

App @Orbycius Streaming Core My test implementation project(path: ':core') implementation project(path: ':streaming')

Slide 153

Slide 153 text

App @Orbycius Streaming Core My test implementation project(path: ':core') implementation project(path: ':streaming') My test

Slide 154

Slide 154 text

App @Orbycius Streaming Core My test implementation project(path: ':core') implementation project(path: ':streaming') Core test

Slide 155

Slide 155 text

App @Orbycius Streaming Core My test implementation project(path: ':core') implementation project(path: ':streaming') Core test Core test

Slide 156

Slide 156 text

App @Orbycius Streaming Core My test Core test implementation project(path: ':streaming') implementation project(path: ':core')

Slide 157

Slide 157 text

App @Orbycius Streaming Core My test My test Core test implementation project(path: ':streaming') implementation project(path: ':core')

Slide 158

Slide 158 text

App @Orbycius Streaming Core My test Core test implementation project(path: ':streaming') implementation project(path: ':core') App test

Slide 159

Slide 159 text

App @Orbycius Streaming Core My test App test Core test implementation project(path: ':streaming') implementation project(path: ':core') App test

Slide 160

Slide 160 text

@Orbycius android { compileSdkVersion androidCompileSdkVersion defaultConfig { minSdkVersion androidMinSdkVersion targetSdkVersion androidTargetSdkVersion versionCode 1 versionName "1.0" } }

Slide 161

Slide 161 text

@Orbycius android { compileSdkVersion androidCompileSdkVersion defaultConfig { minSdkVersion androidMinSdkVersion targetSdkVersion androidTargetSdkVersion versionCode 1 versionName "1.0" } resourcePrefix 'my_prefix' }

Slide 162

Slide 162 text

@Orbycius My other test Resource named '`test`' does not start with the project's resource prefix '`my_prefix`'; rename to '`my_prefixTest`' ? !

Slide 163

Slide 163 text

@Orbycius My other test Resource named '`test`' does not start with the project's resource prefix '`my_prefix`'; rename to '`my_prefixTest`' ? !

Slide 164

Slide 164 text

@Orbycius Android Studio Scopes

Slide 165

Slide 165 text

@Orbycius

Slide 166

Slide 166 text

@Orbycius

Slide 167

Slide 167 text

@Orbycius

Slide 168

Slide 168 text

@Orbycius Recap

Slide 169

Slide 169 text

@Orbycius App is the glue that holds all together

Slide 170

Slide 170 text

@Orbycius Treat your feature modules as libraries

Slide 171

Slide 171 text

@Orbycius “Everything should be as simple as possible, but not simpler” Albert Einstein

Slide 172

Slide 172 text

@Orbycius THANKS https://github.com/marcosholgado/dagger-playground (blog) http://bit.ly/android-module