Slide 1

Slide 1 text

Why we failed at modularizing “our app” An honest retrospective DIRECTED BY Marcos Holgado @Orbycius

Slide 2

Slide 2 text

Why we failed at modularizing SkySports An honest retrospective DIRECTED BY Marcos Holgado @Orbycius

Slide 3

Slide 3 text

THE FOLLOWING TALK HAS BEEN APPROVED FOR APPROPIATE AUDIENCES BY THE DEVELOPERS ASSOCIATION OF AMERICA THE TALK ADVERTISED HAS BEEN RATED PG-25 DEVELOPERS STRONGLY CAUTIONED SOME MATERIAL MAY BE INAPPROPRIATE FOR DEVELOPERS UNDER 25 SOME AWFUL CODE AND BAD PRACTISES

Slide 4

Slide 4 text

Marcos Holgado

Slide 5

Slide 5 text

Marcos Holgado @Orbycius

Slide 6

Slide 6 text

Marcos Holgado @Orbycius DuckDuckGo

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

@Orbycius

Slide 9

Slide 9 text

@Orbycius

Slide 10

Slide 10 text

@Orbycius

Slide 11

Slide 11 text

@Orbycius

Slide 12

Slide 12 text

@Orbycius

Slide 13

Slide 13 text

@Orbycius

Slide 14

Slide 14 text

@Orbycius

Slide 15

Slide 15 text

@Orbycius

Slide 16

Slide 16 text

How does it work? @Orbycius

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

App src main germany uk italia international @Orbycius

Slide 23

Slide 23 text

src main uk germany @Orbycius

Slide 24

Slide 24 text

src main uk germany Feature 1 @Orbycius

Slide 25

Slide 25 text

src main uk germany Feature 1 @Orbycius

Slide 26

Slide 26 text

src main uk germany Feature 1 Feature 2 @Orbycius

Slide 27

Slide 27 text

src main uk germany Feature 1.1 Feature 2 @Orbycius

Slide 28

Slide 28 text

src main uk germany Feature 1.1 Feature 2 @Orbycius

Slide 29

Slide 29 text

src main uk germany Feature 1.1 Feature 2 @Orbycius

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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 @Orbycius

Slide 32

Slide 32 text

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 @Orbycius

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Convincing people is hard @Orbycius

Slide 35

Slide 35 text

Convincing people out of your team is hard @Orbycius

Slide 36

Slide 36 text

“The Vision” @Orbycius

Slide 37

Slide 37 text

Showcase App @Orbycius

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

What is a module? @Orbycius

Slide 45

Slide 45 text

Feature modules Core @Orbycius

Slide 46

Slide 46 text

Feature modules Core @Orbycius

Slide 47

Slide 47 text

@Orbycius

Slide 48

Slide 48 text

Inputs @Orbycius

Slide 49

Slide 49 text

Inputs Outputs @Orbycius

Slide 50

Slide 50 text

Inputs Outputs listeners() @Orbycius

Slide 51

Slide 51 text

Inputs Outputs listeners() App @Orbycius

Slide 52

Slide 52 text

Inputs Outputs listeners() App implements @Orbycius

Slide 53

Slide 53 text

What is a feature? @Orbycius

Slide 54

Slide 54 text

Inputs Outputs App Article List @Orbycius

Slide 55

Slide 55 text

Inputs Outputs ArticleListActivity App Article List @Orbycius

Slide 56

Slide 56 text

Inputs Outputs ArticleListActivity App Article List ArticleWebViewActivity? @Orbycius

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

The first module @Orbycius

Slide 61

Slide 61 text

App @Orbycius

Slide 62

Slide 62 text

App Streaming @Orbycius

Slide 63

Slide 63 text

App Streaming @Orbycius

Slide 64

Slide 64 text

App Streaming Live TV @Orbycius

Slide 65

Slide 65 text

App Streaming Live TV @Orbycius

Slide 66

Slide 66 text

App Streaming Live TV @Orbycius

Slide 67

Slide 67 text

Extremely complicated feature @Orbycius

Slide 68

Slide 68 text

Extremely complicated feature Hard deadline @Orbycius

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Start with the easiest @Orbycius

Slide 72

Slide 72 text

@Orbycius

Slide 73

Slide 73 text

Splash Screen @Orbycius

Slide 74

Slide 74 text

Start with the most valuable @Orbycius

Slide 75

Slide 75 text

Integrate ASAP @Orbycius

Slide 76

Slide 76 text

@Orbycius

Slide 77

Slide 77 text

@Orbycius

Slide 78

Slide 78 text

Don’t compromise your modules @Orbycius

Slide 79

Slide 79 text

Dependency Injection @Orbycius

Slide 80

Slide 80 text

App Streaming Live TV Streaming player @Orbycius

Slide 81

Slide 81 text

App Streaming Live TV Core Streaming player @Orbycius

Slide 82

Slide 82 text

App Streaming Live TV Core Streaming player @Orbycius

Slide 83

Slide 83 text

App Streaming Live TV Core Streaming player @Orbycius

Slide 84

Slide 84 text

App Streaming Live TV Core Streaming player User @Orbycius

Slide 85

Slide 85 text

App Streaming Live TV Core Streaming player User User @Orbycius

Slide 86

Slide 86 text

App Streaming Live TV Core Streaming player User User @Orbycius

Slide 87

Slide 87 text

App Streaming Live TV Core Streaming player User User @Orbycius

Slide 88

Slide 88 text

Dagger to the rescue @Orbycius

Slide 89

Slide 89 text

Dagger to the rescue ? @Orbycius

Slide 90

Slide 90 text

Dagger vs Koin @Orbycius

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

SODD @Orbycius

Slide 93

Slide 93 text

Stack Overflow Driven Development @Orbycius

Slide 94

Slide 94 text

@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 @Orbycius

Slide 95

Slide 95 text

@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 @Orbycius

Slide 96

Slide 96 text

@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 @Orbycius

Slide 97

Slide 97 text

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() @Orbycius

Slide 98

Slide 98 text

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() @Orbycius

Slide 99

Slide 99 text

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() @Orbycius

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

Subcomponents @Orbycius

Slide 104

Slide 104 text

App 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 @Orbycius

Slide 105

Slide 105 text

App 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 @Orbycius

Slide 106

Slide 106 text

App 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 @Orbycius

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

Subcomponents @Orbycius

Slide 111

Slide 111 text

Subcomponents @Orbycius

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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; } } @Orbycius

Slide 116

Slide 116 text

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() @Orbycius

Slide 117

Slide 117 text

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() @Orbycius

Slide 118

Slide 118 text

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() @Orbycius

Slide 119

Slide 119 text

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() @Orbycius

Slide 120

Slide 120 text

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() @Orbycius

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

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"); } } } @Orbycius

Slide 123

Slide 123 text

Understand what you do Share knowledge @Orbycius

Slide 124

Slide 124 text

KI S S @Orbycius

Slide 125

Slide 125 text

K I S S Keep It Simple Stupid @Orbycius

Slide 126

Slide 126 text

Too smart to fail? @Orbycius

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

@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(); } @Orbycius

Slide 129

Slide 129 text

@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; } @Orbycius

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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(); } @Orbycius

Slide 132

Slide 132 text

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; } @Orbycius

Slide 133

Slide 133 text

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(); } @Orbycius

Slide 134

Slide 134 text

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"; } @Orbycius

Slide 135

Slide 135 text

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; } @Orbycius

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

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

Slide 138

Slide 138 text

Core @Orbycius

Slide 139

Slide 139 text

Core do you need it? @Orbycius

Slide 140

Slide 140 text

App Streaming Core User User @Orbycius

Slide 141

Slide 141 text

App Streaming Core User User @Orbycius

Slide 142

Slide 142 text

App Streaming Core User About @Orbycius

Slide 143

Slide 143 text

App Streaming Core User About other_libraries @Orbycius

Slide 144

Slide 144 text

App Streaming Core About other_libraries User User @Orbycius

Slide 145

Slide 145 text

App Streaming Core User User @Orbycius

Slide 146

Slide 146 text

App Streaming Core User User Streaming @Orbycius

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

Treat each module* as a library… maybe @Orbycius

Slide 149

Slide 149 text

Testing @Orbycius

Slide 150

Slide 150 text

Showcase App @Orbycius

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

Showcase App @Orbycius

Slide 153

Slide 153 text

Showcase App @Orbycius

Slide 154

Slide 154 text

Sample Apps @Orbycius

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

Resource conflicts @Orbycius

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

Android Studio Scopes @Orbycius

Slide 170

Slide 170 text

@Orbycius

Slide 171

Slide 171 text

@Orbycius

Slide 172

Slide 172 text

@Orbycius

Slide 173

Slide 173 text

Recap @Orbycius

Slide 174

Slide 174 text

App is the glue that holds all together @Orbycius

Slide 175

Slide 175 text

Treat your feature modules as libraries @Orbycius

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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