SlideShare una empresa de Scribd logo
1 de 148
Descargar para leer sin conexión
Scaling an Android App from
1 to 100 Developers with
Modularization
@BenSchwab13
A tale of two apps.
Around June of 2011 two android apps were being built…
Splinky
apply plugin: ‘com.android.application'
Airbnb
apply plugin: ‘com.android.application'
Airbnb
Airbnb
Airbnb
Airbnb
Splinky’s
Last Stand
150
What is modularization?
apply plugin: ‘com.android.application'
App
What is modularization?
apply plugin: ‘com.android.application'
apply plugin: ‘com.android.library’
App
Lib
What is modularization?
Manifest R res
classes.jar
apply plugin: ‘com.android.application'
apply plugin: ‘com.android.library’
App
Lib
apk/aab
Manifest R res
classes.jar
What is modularization?
apply plugin: ‘com.android.application'
apply plugin: ‘com.android.library’
“App depends on Lib”
App
Lib
What is modularization?
Lib2
Lib3
App
Lib1
The first rule of modularization is…
You don’t modularize if you don’t have too.
Project Structure
Report Card
Project Structure
Report Card
Build times
Build times.
Project/gradle.properties
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/
multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true
Clean Builds
Time(seconds)
0
50
100
150
200
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
1 2 3 4# of modules
Project Structure
Report Card
Build times
Code Ownership
Code Ownership (Attribution)
how effort durability
line git blame
🚌
file/class @Owner(GrowthTeam)
module manifest
Murphy’s Law:
“Whatever can go wrong will go wrong”.
Murphy’s Law of code access:
“Whatever they can access,
they will access
(and it will go wrong)”.
Mall
Code Ownership
(Encapsulation)
Ben’s
Coffee Shop
Mall
Code Ownership
(Encapsulation)
Ben’s
Coffee Shop
CoffeeGrinder
Code Ownership
(Encapsulation)
Mall
Murphy’s
Coffee Shop
Ben’s
Coffee Shop
CoffeeGrinder
Code Ownership
(Encapsulation)
Mall
Murphy’s
Coffee Shop
Ben’s Boba
Shop
BobaMachine
Code Ownership
(Encapsulation)
Mall
Murphy’s
Boba Shop?
Ben’s Boba
Shop
BobaMachine
Code Ownership
(Encapsulation)
internal
public
private
protected
Murphy’s
Coffee Shop
Mall
Ben’s Boba
Shop
Boba Machine
Code Ownership
(Encapsulation)
internal
public
private
protected
Mall
Murphy’s
Coffee Shop
Ben’s Boba
Shop
Boba Machine
Project Structure
Report Card
Build times
Code Ownership
App Bundles
Bigger =/= Better
For every 6mb of app size, install conversion
drops by 1%.
Cutting an 100mb app to 10mb would
see download conversion increase by 30%.
The state of modularization at
Airbnb
160+
modules
2-5
minutes
“Lite”
builds
Lots of
lessons
learned…
Airbnb
experiences
listing
homes
listing
Structure your code link your organization
Conway’s Law.
Base
Modularize by feature.
Base (Core)
• “Pure” Infrastructure.
• Strictly no domain knowledge.
• “Could this be open source-able?”
• Owned by Native Infrastructure team.
• Keep it lean. No deprecated code.
Home Listing
Experience
Listing
Modularize by feature.
Feature Modules
•Owned by a single team.
•Encapsulates a single “feature”.
•A single, addressable entry-point.
•Smaller is better.
•A team might own many feature modules.
•Can not depend on other feature modules.
Base
Airbnb
Home Listing
Experience
Listing
Modularize by feature.
App shell
•No feature-specific code.
•No infrastructure-specific code.
•Creates Dagger Component. Base
Project Structure
Report Card
Build times
Code Ownership
App Bundles
Modularize by feature.
Airbnb
Home Listing
Base
Experience Listing
Modularize by feature.
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Build times?
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Project Structure
Report Card
Build times
Code Ownership
App Bundles
🏎
Ownership?
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Ownership?
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Ownership?
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Project Structure
Report Card
Build times
Code Ownership
App Bundles
🏎
🤝
Instant Apps / Dynamic Features?
Airbnb
Home Listing
Base
Experience
Listing
Place
List your
space
Manage your
space
Profile Payments Itinerary
Project Structure
Report Card
Build times
Code Ownership
App Bundles
🏎
🤝
✅
Common Challenges
Sharing CodeNavigation Upstrea
Common Challenges
Sharing Code
Navigation
Upstreaming depend
fun launch(context: Context, listingId: Long) {
val listingIntent = Intent(context, HomeListingActivity::class.java).apply {
putExtra("listingId", listingId)
}
context.startActivity(listingIntent)
}
Airbnb
Base
Home Listing Experience Listing
Nearby Experiences
fun launch(context: Context, listingId: Long) {
val listingIntent = Intent(context, HomeListingActivity::class.java).apply {
putExtra("listingId", listingId)
}
context.startActivity(listingIntent)
}
Airbnb
Base
Home Listing Experience Listing
Nearby Experiences
fun launch(context: Context, listingId: Long) {
val listingIntent = Intent(context, ExperienceListingActivity::class.java).apply {
putExtra("listingId", listingId)
}
context.startActivity(listingIntent)
}
Airbnb
Base
Home Listing Experience Listing
Nearby Experiences
fun launch(context: Context, listingId: Long) {
val listingIntent = Intent(context, ExperienceListingActivity::class.java).apply {
putExtra("listingId", listingId)
}
context.startActivity(listingIntent)
}
Airbnb
Base
Home Listing Experience Listing
Nearby Experiences
fun launch(context: Context, listingId: Long) {
val listingIntent = Intent(context, ExperienceListingActivity::class.java).apply {
putExtra("listingId", listingId)
}
context.startActivity(listingIntent)
}
Airbnb
Base
Home Listing Experience Listing
Nearby Experiences
Airbnb
Base
Home Listing Experience Listing
Intents
fun <T> loadClassOrNull(className: String): Class<T>? {
return CLASS_MAP.getOrPut(className) {
try {
Class.forName(className)
} catch (e: ClassNotFoundException) {
// Can't store a null value in the concurrent map
return null
}
}.castOrNull()
object Activities {
fun intentForListing(context: Context, listingId: Long): Intent? {
val args = Bundle().apply { putLong("listingId", listingId) }
return loadClassOrNull<Activity>("com.airbnb.android.ListingActivity")
.let { Intent(context, it) }
.apply { putExtras(args) }
}
}
object Fragments {
fun fragmentForListing(listingId: Long): Fragment? {
val args = Bundle().apply { putLong("listingId", listingId) }
return loadClassOrNull<Fragment>("com.airbnb.android.ListingFragment")
object Activities {
fun intentForListing(context: Context, listingId: Long): Intent? {
val args = Bundle().apply { putLong("listingId", listingId) }
return loadClassOrNull<Activity>("com.airbnb.android.ListingActivity")
.let { Intent(context, it) }
.apply { putExtras(args) }
}
}
object Fragments {
fun fragmentForListing(listingId: Long): Fragment? {
val args = Bundle().apply { putLong("listingId", listingId) }
return loadClassOrNull<Fragment>("com.airbnb.android.ListingFragment")
?.newInstance()
?.apply { this.arguments?.putBundle("args", args) }
}
}
object Fragments {
fun fragmentForListing(listingId: Long): Fragment? {
val args = Bundle().apply { putLong("listingId", listingId) }
return loadClassOrNull<Fragment>("com.airbnb.android.ListingFragment")
?.newInstance()
?.apply { this.arguments?.putBundle("args", args) }
}
}
Intent?
•This is a dynamic feature. Need to download it with PlayCore.
•This is debug build and the module is not present. Toast developer.
•A developer deleted the activity/fragment.
Reflect on all fragment/activity entries and assert present.
Airbnb
Base
Home Listing Experience Listing
Intents
Common Challenges
Sharing Code
Navigation
Upstreaming depend
Common Challenges
Sharing Code
Navigation Upstreaming dependencies
Home Listing
Experience
Listing
Wishlist Intents
Airbnb
Base
*Same dependencies as before. I’m just lazy…
Airbnb
Base
Home Listing
Experience
Listing
Wishlist Intents
Wishlist
Manager
Home Listing
Experience
Listing
Wishlist Intents
Airbnb
Base
Wishlist
Manager
Base
Wishlist
Manager
SearchFilters
ListingFormat
ter
Home Listing
Experience
Listing
Wishlist Intents
Airbnb
Library Modules
lib.WishlistManager
• Owned by a single team.
• No launchable features.
• Provides consumable
dependencies via an interface.
• Forces API design, instead of
sticking if/when cases in other
teams code.
Base
Airbnb
Home ExperienceWishlist Intents
Keep your base lean.Keep your base lean.
Communicating Deprecation
Base
Home Listing
Experience
Listing
Wishlist
Communicating Deprecation
Base
Guava
Home Listing
Experience
Listing
Wishlist
Communicating Deprecation
Base
Guava
Home Listing
Experience
Listing
Wishlist
Murphy’s Law of code access:
“Whatever they can access,
they will access
(and it will go wrong)”.
Communicating Deprecation
Base
Guava
Home Listing
Experience
Listing
Wishlist
Communicating Deprecation
Base
lib.deprecated.Guava
Home Listing
Experience
Listing
Wishlistnew feature
Common Challenges
Sharing Code
Navigation Upstreaming dependencies
Common Challenges
Sharing CodeNavigation
Upstreaming dependencies
Airbnb
Experience ListingHome Listing
class TrebuchetRequest(val keys: Set<TrebuchetKey>)
Base
Experience ListingHome Listing
Base
enum class HomeListingTrebuchetKeys(override val key: String) : TrebuchetKey {
ShowSimilarExperiences(“android.show_similiar_experiences”),
UseMvRx(“android.use_mvrx_home_listing”),
}
Airbnb
Experience ListingHome Listing
enum class ExperienceListingTrebuchetKeys(override val key: String): TrebuchetKey {
ShowSimilarListings(“android.show_similiar_listings”),
UseVideos(“android.use_videos”),
}
Base
Airbnb
Experience ListingHome Listing
Base
Airbnb
class TrebuchetRequest(val keys: Set<TrebuchetKey>)
class TrebuchetKey
Experience ListingHome Listing
Base
Airbnb
Violates no infrastructure in shell.
Violates single module owner.
class TrebuchetRequest(val keys: Set<TrebuchetKey>)
class TrebuchetKey
Experience ListingHome Listing
Base
Airbnb
val trebuchetKeys =
“Plugin” Architecture
class TrebuchetRequest(…)
class TrebuchetKey
interface BaseGraph {
val trebuchetKeys: Set<TrebuchetKey>
}
Experience ListingHome Listing
Base
Airbnb
“Plugin” Architecture
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
Experience ListingHome Listing
Base
Airbnb
“Plugin” Architecture
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
Experience ListingHome Listing
Base
Airbnb
“Plugin” Architecture
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
Experience ListingHome Listing
Base
Airbnb
“Plugin” Architecture
Experience ListingHome Listing
Base
Airbnb
“Plugin” Architecture
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
class AirbnbApplication : Application() {
override fun onCreate() {
val baseGraph = object : BaseGraph {
override val trebuchetKeys: Set<TrebuchetKey>
get() {
return mutableSetOf<TrebuchetKey>().apply {
addAll(HomeListingTrebuchetKeys.values())
addAll(Feature1TrebuchetKeys.values())
…
addAll(FeatureNTrebuchetKeys.values())
addAll(ExperienceListingTrebuchetKeys.values())
}
}
}
BaseApplication.onTargetApplicationCreated(this, baseGraph)
}
}
Experience
Listing
Home
Listing
Base
Airbnb
“Plugin” Architecture
Feature1Feature1
Managing Multi-module
Projects with Dagger
Airbnb
ExperienceListingHomeListing
Airbnb
AirbnbComponent
ExperienceListingHomeListing
HomeListingComponent ExperienceListingComponent
@ComponentScope
@Singleton
“App scope”
Base
Airbnb
AirbnbComponent
ExperienceListingHomeListing
HomeListingComponent ExperienceListingComponent
@ComponentScope
@Singleton
“App scope”
Base
Airbnb
AirbnbComponent
ExperienceListingHomeListing
HomeListingComponent ExperienceListingComponent
@ComponentScope
@Singleton
“App scope”
Base
AppModule AppModule
@Singleton
“App scope”
@Singleton
“App scope”
Airbnb
AirbnbComponent : AppGraph
ExperienceListingHomeListing
HomeListingComponent ExperienceListingComponent
Base
AppModule AppModule
AppGraph
@Multibinds @Multibinds
Unified Dagger System.
interface BaseGraph {
fun trebuchetKeys(): Set<TrebuchetKey>
}
@Module
abstract class BaseAppModule {
@Multibinds abstract fun trebuchetKeys(): Set<TrebuchetKey>
}
class HomeListingDagger {
@Subcomponent
@ComponentScope
interface HomeListingComponent {
@Subcomponent.Builder
interface Builder {
fun build(): HomeListingComponent
}
}
@Module
abstract class AppModule {
@Binds
@ElementsIntoSet
fun provideTrebuchetKeys() : Set<Trebuchet> {
return HomeListingTrebuchetKeys.values().toSet()
}
}
interface HomeListingAppGraph {
fun p3Builder(): HomeListingComponent.Builder
}
}
Base
Features
Airbnb
}
interface AppGraph
}
interface BaseGraph {
fun trebuchetKeys(): Set<TrebuchetKey>
}
@Module
abstract class BaseAppModule {
@Multibinds abstract fun trebuchetKeys(): Set<Trebuchet
}
class HomeListingDagger {
@Subcomponent
@ComponentScope
interface HomeListingComponent {
@Subcomponent.Builder
interface Builder {
fun build(): HomeListingComponent
}
}
@Module
abstract class AppModule {
@Binds
@ElementsIntoSet
fun provideTrebuchetKeys() : Set<Trebuchet> {
return HomeListingTrebuchetKeys.values().toSet()
}
}
interface HomeListingAppGraph {
fun p3Builder(): HomeListingComponent.Builder
}
}
Base
Features
Airbnb
}
interface AppGraph
}
class HomeListingDagger {
@Subcomponent
@ComponentScope
interface HomeListingComponent {
@Subcomponent.Builder
interface Builder {
fun build(): HomeListingComponent
}
}
@Module
abstract class AppModule {
@Binds
@ElementsIntoSet
fun provideTrebuchetKeys() : Set<Trebuchet> {
return HomeListingTrebuchetKeys.values().toSet()
}
}
interface AppGraph {
fun p3Builder(): HomeListingComponent.Builder
}
Features
Airbnb
interface AppGraph
}
class HomeListingDagger {
@Subcomponent
@ComponentScope
interface HomeListingComponent {
@Subcomponent.Builder
interface Builder {
fun build(): HomeListingComponent
}
}
@Module
abstract class AppModule {
@Binds
@ElementsIntoSet
fun provideTrebuchetKeys() : Set<Trebuchet> {
return HomeListingTrebuchetKeys.values().toSet()
}
}
interface AppGraph {
fun p3Builder(): HomeListingComponent.Builder
}
Features
Airbnb
interface AppGraph
}
class HomeListingDagger {
@Subcomponent
@ComponentScope
interface HomeListingComponent {
@Subcomponent.Builder
interface Builder {
fun build(): HomeListingComponent
}
}
@Module
abstract class AppModule {
@Binds
@ElementsIntoSet
fun provideTrebuchetKeys() : Set<Trebuchet> {
return HomeListingTrebuchetKeys.values().toSet()
}
}
interface AppGraph {
fun p3Builder(): HomeListingComponent.Builder
}
Features
Airbnb
interface AppGraph
}
Airbnb
@Singleton
@Component(modules = [
HomeListingDagger.HomeListingAppModule::class,
ExploreListingDagger.ExploreListingAppModule::class
])
interface AirbnbComponent :
BaseGraph,
HomeListingDagger.HomeListingAppGraph,
ExploreListingDagger.AppGraph
Airbnb
@Singleton
@Component(modules = [
HomeListingDagger.HomeListingAppModule::class,
ExploreListingDagger.ExploreListingAppModule::class
])
interface AirbnbComponent :
BaseGraph,
HomeListingDagger.HomeListingAppGraph,
ExploreListingDagger.AppGraph
Base fun <T : BaseGraph> component(): T
Home Listing
Experience
Listing
Place
List your
space
Mange your
space
Profile Payments Itinerary
Airbnb
@Singleton
@Component(modules = [
HomeListingDagger.HomeListingAppModule::class,
ExploreListingDagger.ExploreListingAppModule::class
])
interface AirbnbComponent :
BaseGraph,
HomeListingDagger.HomeListingAppGraph,
ExploreListingDagger.AppGraph
Base fun <T : BaseGraph> component(): T
Home Listing Place
List your
space
Mange your
space
Profile ItineraryPayments
Experience
Listing
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Dagger initialization
Airbnb
Build tooling to support your project structure.
The anatomy of a feature module.
Project/homelisting/
Project/experiencelisting/
build.gradle
TrebuchetKeys.kt
HomeListingDagger.kt
Project/airbnb/
AndroidManifest.xml
res/resources
build.gradle
TrebuchetKeys.kt
ExperienceListingDagger.kt
AndroidManifest.xml
res/resources
AirbnbGraph
AirbnbComponent
build.gradle
settings.gradle
The anatomy of a feature module.
Project/homelisting/
Project/experiencelisting/
build.gradle
TrebuchetKeys.kt
HomeListingDagger.kt
Project/airbnb/
AndroidManifest.xml
res/resources
build.gradle
TrebuchetKeys.kt
ExperienceListingDagger.kt
AndroidManifest.xml
res/resources
AirbnbGraph
AirbnbComponent
build.gradle
settings.gradle
package <%= module_info.qualified_package_name %>
import com.airbnb.android.base.trebuchet.TrebuchetKey
enum class <%= module_info.name_pascal_case %>TrebuchetKeys(override val key: String) : TrebuchetKey
package <%= module_info.qualified_package_name %>
import com.airbnb.android.base.trebuchet.TrebuchetKey
enum class <%= module_info.name_pascal_case %>TrebuchetKeys(override val key: String) : TrebuchetKey
package <%= module_info.qualified_package_name %>
import com.airbnb.android.base.trebuchet.TrebuchetKey
enum class <%= module_info.name_pascal_case %>TrebuchetKeys(override val key: String) : TrebuchetKey
{
…
'TrebuchetKeys.kt' => “#{module_info.main_dir}/#{module_info.name_pascal_case}TrebuchetKeys.kt",
…
}.each do |template, file_name|
erb_template = ERB.new(File.read("#{template_dir}/#{template}"), nil, '-')
File.write(file_name, erb_template.result(binding))
end
ml006617bschwab:android ben_schwab$ bundle exec rake make_module
Module name (with spaces)
home listing
Creating home listing with package name com.airbnb.android.homelisting
Will you be moving/writing Java code in this module? [y/n]
n
Add home listing as a dependency of the flavor.full module? [y/n]
n
Run `./buckw project --skip-build` for intellij to pick up the new module.
Airbnb “Lite”
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
4 5 6 7 8 9 10# of modules
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
4 5 6 7 8 9 10# of modules
Project Structure
Report Card
Build times
Code Ownership
App Bundles
🏎
🤝
✅
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
4 5 6 7 8 9 10# of modules
Project Structure
Report Card
Build times
Code Ownership
App Bundles
🤝
✅
🐢
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
4 5 6 7 8 9 10# of modules
What is a flavor?
build.gradle
android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // This property is optional if you are using only one dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}
build.gradle
android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // This property is optional if you are using only one dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}
./gradlew :airbnb:installDemoDebug
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Flavors flavor.homes flavor.experiencesflavor.full
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Flavors flavor.homes flavor.experiencesflavor.full
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Flavors flavor.homes flavor.experiencesflavor.full
Airbnb
home listing experiencewishlist
lib.wishlistmanager
base
intents
flavor.homes flavor.experiencesflavor.full
Application Shell
Features
Infrastructure
Libraries
Flavors
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Hundreds of source files
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
Full build
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Time
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
Full build
Clean Builds
Time(seconds)
0
12.5
25
37.5
50
Time
.2 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
Full Build Lite Flavor
1) It needs to be easy to create a flavor.
project.flavors.each { flavor, config ->
project.dependencies.add("${flavor}Api", project(config.entryModule))
}
project.ext.flavors = [
// If you want your flavor to be installed as a separate app for side-by-side
installation, do:
// foo: new FlavorOptions(":favor.foo").useSeparatePackageName()
full: new FlavorOptions(":flavor.full"),
homeListing: new FlavorOptions(“:flavor.homelisting”).useSeparatePackageName(),
]
Airbnb
productFlavors {
project.flavors.each { flavor, config ->
"$flavor" {
dimension 'scope'
if (flavor != 'full') {
versionNameSuffix ".$flavor"
if (config.useSeparatePackageName) {
applicationIdSuffix ".$flavor"
}
}
}
}
}
build.gradle
Specify the the flavor module to install.
project.flavors.each { flavor, config ->
project.dependencies.add("${flavor}Api", project(config.entryModule))
}
Airbnb
productFlavors {
project.flavors.each { flavor, config ->
"$flavor" {
dimension 'scope'
if (flavor != 'full') {
versionNameSuffix ".$flavor"
if (config.useSeparatePackageName) {
applicationIdSuffix ".$flavor"
}
}
}
}
}
build.gradle
Allow side-by-side installation of lite apps.
Create the flavor.
project.flavors.each { flavor, config ->
project.dependencies.add("${flavor}Api", project(config.entryModule))
}
Airbnb build.gradle
project.flavors.each { flavor, config ->
project.dependencies.add("${flavor}Api", project(config.entryModule))
}
Airbnb build.gradle
flavor.homes build.gradle
dependencies {
api project(':homelisting')
api project(':hometour')
}
Airbnb
flavor.homes
home listing
lib.wishlistmanag
base
hometour
Generate the dagger component
1) It needs to be easy to create a flavor.
2) Flavors need to handle missing code gracefully.
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Flavors flavor.homes flavor.experiencesflavor.full
AirbnbApplication Shell
Features
Infrastructure
Libraries
home listing experiencewishlist
lib.wishlistmanager
base
intents
Flavors flavor.homes flavor.experiencesflavor.full
3) Can flavors be more than just faster?
1) It needs to be easy to create a flavor.
2) Flavors need to handle missing code gracefully.
Custom Launchers
Module Unloading
@gpeal8Special thanks to:
MvRx
GraphQL
@BenSchwab13
Modularization
Epoxy
Server Driven UI

Más contenido relacionado

La actualidad más candente

La actualidad más candente (20)

Lecture 12: React-Native Firebase Authentication
Lecture 12: React-Native Firebase AuthenticationLecture 12: React-Native Firebase Authentication
Lecture 12: React-Native Firebase Authentication
 
Empire Kurulumu ve Kullanımı
Empire Kurulumu ve Kullanımı Empire Kurulumu ve Kullanımı
Empire Kurulumu ve Kullanımı
 
LIA 172_final.pdf
LIA 172_final.pdfLIA 172_final.pdf
LIA 172_final.pdf
 
Excellent Qualities of Marriage
Excellent Qualities of MarriageExcellent Qualities of Marriage
Excellent Qualities of Marriage
 
Web ve Mobil Uygulama Güvenlik Testleri Eğitimi Uygulama Kitabı
Web ve Mobil Uygulama Güvenlik Testleri Eğitimi Uygulama KitabıWeb ve Mobil Uygulama Güvenlik Testleri Eğitimi Uygulama Kitabı
Web ve Mobil Uygulama Güvenlik Testleri Eğitimi Uygulama Kitabı
 
Güvenlik Testlerinde Açık Kaynak İstihbaratı Kullanımı
Güvenlik Testlerinde Açık Kaynak İstihbaratı KullanımıGüvenlik Testlerinde Açık Kaynak İstihbaratı Kullanımı
Güvenlik Testlerinde Açık Kaynak İstihbaratı Kullanımı
 
Open Source Soc Araçları Eğitimi 2020-II
Open Source Soc Araçları Eğitimi 2020-IIOpen Source Soc Araçları Eğitimi 2020-II
Open Source Soc Araçları Eğitimi 2020-II
 
İleri Seviye Ağ Güvenliği Lab Kitabı
İleri Seviye Ağ Güvenliği Lab Kitabıİleri Seviye Ağ Güvenliği Lab Kitabı
İleri Seviye Ağ Güvenliği Lab Kitabı
 
PERJANJIAN KOMPENSASI _CLEAN DRAFT.docx
PERJANJIAN KOMPENSASI _CLEAN DRAFT.docxPERJANJIAN KOMPENSASI _CLEAN DRAFT.docx
PERJANJIAN KOMPENSASI _CLEAN DRAFT.docx
 
Beyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab KitabıBeyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab Kitabı
 
There’s an OpenBullet Attack Config for Your Site – What Should You Do?
There’s an OpenBullet Attack Config for Your Site – What Should You Do?There’s an OpenBullet Attack Config for Your Site – What Should You Do?
There’s an OpenBullet Attack Config for Your Site – What Should You Do?
 
Contoh Perjanjian Jaringan Akses Kabel Terbaru (Beli Perjanjian, Hub: 0811888...
Contoh Perjanjian Jaringan Akses Kabel Terbaru (Beli Perjanjian, Hub: 0811888...Contoh Perjanjian Jaringan Akses Kabel Terbaru (Beli Perjanjian, Hub: 0811888...
Contoh Perjanjian Jaringan Akses Kabel Terbaru (Beli Perjanjian, Hub: 0811888...
 
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 1
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 1Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 1
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 1
 
Tersine Mühendislik 101
Tersine Mühendislik 101Tersine Mühendislik 101
Tersine Mühendislik 101
 
Android Hacking
Android HackingAndroid Hacking
Android Hacking
 
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesiAndroid Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
 
Siber Saldırılar i̇çin Erken Uyarı Sistemi
Siber Saldırılar i̇çin Erken Uyarı SistemiSiber Saldırılar i̇çin Erken Uyarı Sistemi
Siber Saldırılar i̇çin Erken Uyarı Sistemi
 
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 13, 14, 15
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 13, 14, 15Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 13, 14, 15
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 13, 14, 15
 
Beyaz Şapkalı Hacker CEH Eğitimi - Aktif Bilgi Toplama
Beyaz Şapkalı Hacker CEH Eğitimi - Aktif Bilgi ToplamaBeyaz Şapkalı Hacker CEH Eğitimi - Aktif Bilgi Toplama
Beyaz Şapkalı Hacker CEH Eğitimi - Aktif Bilgi Toplama
 
SOC Kurulumu ve Yönetimi İçin Açık Kaynak Kodlu Çözümler
SOC Kurulumu ve Yönetimi İçin Açık Kaynak Kodlu ÇözümlerSOC Kurulumu ve Yönetimi İçin Açık Kaynak Kodlu Çözümler
SOC Kurulumu ve Yönetimi İçin Açık Kaynak Kodlu Çözümler
 

Similar a Scaling your Android App With Modularization

Building native mobile apps with word press
Building native mobile apps with word pressBuilding native mobile apps with word press
Building native mobile apps with word press
Nikhil Vishnu P.V
 
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptxLecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
NgLQun
 

Similar a Scaling your Android App With Modularization (20)

Cross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic FrameworkCross Platform Mobile Apps with the Ionic Framework
Cross Platform Mobile Apps with the Ionic Framework
 
Playing with parse.com
Playing with parse.comPlaying with parse.com
Playing with parse.com
 
Prairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API ResponsesPrairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API Responses
 
Building native mobile apps with word press
Building native mobile apps with word pressBuilding native mobile apps with word press
Building native mobile apps with word press
 
From System Engineer to Gopher
From System Engineer to GopherFrom System Engineer to Gopher
From System Engineer to Gopher
 
Build your-own-instagram-filters-with-javascript-202-335 (1)
Build your-own-instagram-filters-with-javascript-202-335 (1)Build your-own-instagram-filters-with-javascript-202-335 (1)
Build your-own-instagram-filters-with-javascript-202-335 (1)
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreAutomated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
 
Session-1.pptx
Session-1.pptxSession-1.pptx
Session-1.pptx
 
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptxLecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
 
Compose Camp: Introduction to Kotlin.pptx
Compose Camp: Introduction to Kotlin.pptxCompose Camp: Introduction to Kotlin.pptx
Compose Camp: Introduction to Kotlin.pptx
 
Internal Android Library Management (DroidCon SF 2016, Droidcon Italy 2016)
Internal Android Library Management (DroidCon SF 2016, Droidcon Italy 2016)Internal Android Library Management (DroidCon SF 2016, Droidcon Italy 2016)
Internal Android Library Management (DroidCon SF 2016, Droidcon Italy 2016)
 
Workshop 15: Ionic framework
Workshop 15: Ionic frameworkWorkshop 15: Ionic framework
Workshop 15: Ionic framework
 
Building Serverless APIs (January 2017)
Building Serverless APIs (January 2017)Building Serverless APIs (January 2017)
Building Serverless APIs (January 2017)
 
Hosting Your Own OTA Update Service
Hosting Your Own OTA Update ServiceHosting Your Own OTA Update Service
Hosting Your Own OTA Update Service
 
Alfresco Development Framework Basic
Alfresco Development Framework BasicAlfresco Development Framework Basic
Alfresco Development Framework Basic
 
Telerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT ConferenceTelerik AppBuilder Presentation for TelerikNEXT Conference
Telerik AppBuilder Presentation for TelerikNEXT Conference
 
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
 
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
Life After Mobilegeddon: App Deep Linking Strategies - Pubcon October 2015
 
Session-1 edited.pptx
Session-1 edited.pptxSession-1 edited.pptx
Session-1 edited.pptx
 
Progressive Web Application by Citytech
Progressive Web Application by CitytechProgressive Web Application by Citytech
Progressive Web Application by Citytech
 

Último

Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
drm1699
 

Último (20)

Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
Wired_2.0_CREATE YOUR ULTIMATE LEARNING ENVIRONMENT_JCON_16052024
 
Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
Abortion Pills For Sale WhatsApp[[+27737758557]] In Birch Acres, Abortion Pil...
 
Effective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeConEffective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeCon
 
Your Ultimate Web Studio for Streaming Anywhere | Evmux
Your Ultimate Web Studio for Streaming Anywhere | EvmuxYour Ultimate Web Studio for Streaming Anywhere | Evmux
Your Ultimate Web Studio for Streaming Anywhere | Evmux
 
Auto Affiliate AI Earns First Commission in 3 Hours..pdf
Auto Affiliate  AI Earns First Commission in 3 Hours..pdfAuto Affiliate  AI Earns First Commission in 3 Hours..pdf
Auto Affiliate AI Earns First Commission in 3 Hours..pdf
 
Microsoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdfMicrosoft365_Dev_Security_2024_05_16.pdf
Microsoft365_Dev_Security_2024_05_16.pdf
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST API
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
 
Abortion Pill Prices Mthatha (@](+27832195400*)[ 🏥 Women's Abortion Clinic In...
Abortion Pill Prices Mthatha (@](+27832195400*)[ 🏥 Women's Abortion Clinic In...Abortion Pill Prices Mthatha (@](+27832195400*)[ 🏥 Women's Abortion Clinic In...
Abortion Pill Prices Mthatha (@](+27832195400*)[ 🏥 Women's Abortion Clinic In...
 
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
 
Modern binary build systems - PyCon 2024
Modern binary build systems - PyCon 2024Modern binary build systems - PyCon 2024
Modern binary build systems - PyCon 2024
 
Community is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea GouletCommunity is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea Goulet
 
Transformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with LinksTransformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with Links
 
GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with GraphGraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
GraphSummit Milan & Stockholm - Neo4j: The Art of the Possible with Graph
 
The Strategic Impact of Buying vs Building in Test Automation
The Strategic Impact of Buying vs Building in Test AutomationThe Strategic Impact of Buying vs Building in Test Automation
The Strategic Impact of Buying vs Building in Test Automation
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Era
 
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
 
CERVED e Neo4j su una nuvola, migrazione ed evoluzione di un grafo mission cr...
CERVED e Neo4j su una nuvola, migrazione ed evoluzione di un grafo mission cr...CERVED e Neo4j su una nuvola, migrazione ed evoluzione di un grafo mission cr...
CERVED e Neo4j su una nuvola, migrazione ed evoluzione di un grafo mission cr...
 
Prompt Engineering - an Art, a Science, or your next Job Title?
Prompt Engineering - an Art, a Science, or your next Job Title?Prompt Engineering - an Art, a Science, or your next Job Title?
Prompt Engineering - an Art, a Science, or your next Job Title?
 
Abortion Pill Prices Germiston ](+27832195400*)[ 🏥 Women's Abortion Clinic in...
Abortion Pill Prices Germiston ](+27832195400*)[ 🏥 Women's Abortion Clinic in...Abortion Pill Prices Germiston ](+27832195400*)[ 🏥 Women's Abortion Clinic in...
Abortion Pill Prices Germiston ](+27832195400*)[ 🏥 Women's Abortion Clinic in...
 

Scaling your Android App With Modularization