Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

droidcon News
 

droidcon NYC 2019

Share
Tweet

 

Dagger Party Tricks
By
Zac Sweers
droidcon New York City 2019
Dependency injection doesn't have to be all plumbing-doom and tedium-gloom. This lightning talk will cover some simple clever things you can do with Dagger to simplify modularization, improve startup perf, and hide your intermediate dependencies. Warning: talk examples are heavily taco-based. Come hungry to learn.
Transcript
en-us
00:00
[Music]
00:12
my name is Zack Spears I work on a bunch
00:15
of open store stuff like ketchup or
00:18
mocchi or auto dispose I'm currently
00:21
funemployed and working on moving to New
00:23
York which I realized as a technically
00:24
fancy way of saying homeless and
00:26
unemployed and I'm learning some things
00:30
about New York
00:31
well I'm here like the exotic
00:33
pronunciations of words like Houston and
00:35
stiverson and I hope you learned
00:39
something here too so a quick feel for
00:41
the audience here who uses dagger
00:44
alright and that's that basically
00:47
everyone one two anyone used coin I
00:50
won't tell Jake alright
00:54
juice anyone here not used di in any
00:59
form I like how you got a tap on the
01:02
shoulder just to remind you to brace for
01:04
that so if you're not so for anyone who
01:07
doesn't use dagger to some of these work
01:09
and other di patterns to dagger just
01:11
makes it easier so back to the talk
01:14
today I'm gonna be talking about some
01:16
dagger party tricks specifically for
01:18
issue of them
01:20
these aren't meant to be like weird
01:22
trivia or gotchas or anything but rather
01:24
just clever and neat ways that you can
01:25
use a dagger to enable better patterns
01:28
in your code so the first thing I want
01:30
to talk about is bleeding on a strong
01:32
foot here something that I think you can
01:34
take home and do today it's really
01:37
simple and straightforward when I gave
01:40
this talk a few months back I posted
01:42
this to Twitter and basically you know
01:46
letting people know is gonna do it and
01:47
perhaps not surprisingly it turned into
01:49
a bit of a guessing game from some
01:50
people so some of the assets were good
01:53
you know like that fast in it or using
01:56
lazy or static providers one of these is
01:59
on the right track for this some of
02:01
these were on other tracks like make
02:04
everything's to go yeah pick everything
02:06
serializable and loaded start up or kill
02:11
the process on start up
02:13
so they're clever but these aren't what
02:17
I'm getting at the solution that I am
02:19
talking about though is clever but it's
02:21
nothing fancy so who your uses retrofit
02:25
all right who doesn't all right keep
02:29
your hands up anyone that sees them
02:31
befriend them and show them the light
02:33
there's that so here's the same like
02:38
three lines of retrofit that everyone
02:39
pasted into their app it's pretty
02:41
vanilla retrofit and somewhere later
02:42
you'll pass it an Rx to call adapter or
02:45
your master converter but for now we're
02:47
just gonna focus on the pretty basic
02:48
stuff here so chances are your plumbing
02:50
in your okay httpclient here and again
02:53
pretty standard farther up the chain
02:55
will have other provides for the clients
02:56
such as its cache and there you go your
02:59
standard square buffet so who's
03:01
basically got this in their app all
03:04
right cool so who does this at startup
03:10
all right seeing a lot of like shaky
03:12
hands like Queens wave here so that's
03:16
you know around boy I thought most
03:17
people are doing this one startup
03:18
because you got to start you know if you
03:20
log analytics when someone opens your
03:22
app then you're doing this so let's look
03:24
at this a bit in context though so one
03:26
app startup RDI graph is going to
03:27
roughly follow this path where our cash
03:29
is going to get initialized and then
03:31
we're going to go to ok CDP and then
03:34
finally we're gonna feed that into
03:35
retrofit all this gets injected
03:38
somewheres a constructor of our
03:41
imaginary class here and here's the
03:43
kicker this step can take a hundred to
03:46
150 milliseconds to fully start up
03:48
because you've got a lot going on here
03:50
like in OK HTTP there's this trust
03:52
manager Factory that on some devices can
03:54
take a hundred milliseconds in the wild
03:57
whenever you're providing your cache up
03:60
here this I thought I had laser pointer
04:05
for a second that was cool so yeah
04:09
whenever you're creating your cast
04:11
you're actually having to go look at the
04:12
cache directory here it used to be
04:15
initially eager or used to be eagerly
04:18
initialized in ok HTTP as well which
04:21
would then involve some disk i/o and
04:23
even now it still spins up its own
04:25
executor during
04:26
initialization and you're paying all
04:27
this cost up front for something that is
04:29
ultimately not needed maybe I'll start
04:31
up or at the very least going to be on a
04:33
background thread every time you use it
04:34
so what can we do here and the answer is
04:37
lazy lazy as a concept and dagger that
04:40
you've probably seen or even used but if
04:43
we look at our example here where can we
04:44
use this the trivial answer is we could
04:47
just apply it at the injection site and
04:50
this kind of works but you're just
04:51
kicking the can down the road a bit
04:52
because as soon as you use it then boom
04:54
you hit that cost again potentially on
04:57
the main thread or wherever you're
04:58
calling it from there's another way
05:00
there though so you see this client
05:04
retrofit speaks ok CDP and that's
05:07
nothing new what you might not realize
05:09
though is that this is actually just a
05:11
shorthand for another method called call
05:12
Factory now call Factory is an interface
05:15
in ok CDP that ok should defeat client
05:17
is just an implementer of and anything
05:20
else can implement this or you can also
05:21
just do plain simple delegation so more
05:25
simply because it's just a single method
05:26
interface we can put it in a lambda now
05:28
we're cooking with fire so the entire
05:30
client is actually just being wrapped up
05:33
and deferred in this and what that means
05:34
is that we can actually take this client
05:36
and make it lazy and this is actually
05:39
everything that we need for this example
05:41
now we deferred the entire
05:42
initialization of the client to the
05:44
first network called now here's the
05:46
kicker what thread is that called on
05:49
hopefully not the main thread so if
05:52
you're usually something like arc Java
05:53
or co-routines and doing your network
05:55
all off the main thread then that means
05:57
that this entire thing there we go ok so
06:04
this entire thing is now paid off of the
06:07
main thread and it's also amortized by
06:08
whatever the cost of your i/o is so if
06:13
you're doing an epic request that could
06:14
be 300 400 milliseconds if you're in
06:17
India that could be like 10 minutes the
06:20
100 milliseconds that you're paying on
06:22
startup you've now actually kicked to
06:23
whoever's waiting for this network
06:25
request and you know taking all those
06:29
and batching them into and every call
06:31
you're saving yourself a lot of time
06:32
here and in short you've taken all this
06:34
and
06:36
kicked it off to a background thread and
06:39
if you want to be super super sure that
06:40
you're actually getting the behavior
06:42
that you expect you could even stick
06:43
some main thread checks in there so
06:45
switching gears let's talk about modular
06:48
zation so module ization is a great way
06:50
to decoupled dependencies improve build
06:52
times with dagger you've I've got a
06:55
couple of tips for modularizing easily
06:57
first let's take a look at refactoring
06:59
so say I've got this giant module only
07:02
to here but imagine that there's like 50
07:04
and it's full of seasoning and taco
07:07
provisions so today I want to refactor
07:10
this a bit either I want it to be
07:11
smaller or I want to reuse some bits of
07:13
it a common pattern that I see people do
07:16
is do something like this where you're
07:18
pulling it out from the top and you
07:19
leave everything else in this sort of
07:20
base module that exists sort of in
07:23
perpetuity I usually think this is
07:25
actually the wrong thing to do though
07:26
because when you do this then you have
07:28
to update usages for everyone to point
07:30
to your new dependency now everything
07:32
you pull out a base is just going to
07:33
depend on base anyway so no one's really
07:34
getting any benefit of it but it's on
07:37
the right path though yeah the the right
07:39
spirit that we can make a couple tweaks
07:42
to this approach and I think that you'll
07:44
end up with something better so let's
07:46
look at this again but instead of making
07:47
a base module and pulling stuff out from
07:49
the top let's think of it a different
07:50
way and let's pull things out from the
07:52
bottom so in this case let's say pools
07:54
teasing out to its own module that my
07:56
giant module depends on this looks quite
07:59
similar but the key difference here is
08:01
that anyone depending on my giant module
08:03
didn't have to do anything your API for
08:06
consumers of that hasn't changed
08:08
you can continue iterating on this and
08:10
refactoring it under the hood it's all
08:11
just an implementation detail of how its
08:13
composed so this makes refactoring get
08:16
simple you can iterate on this without
08:19
churning people you can take more steps
08:21
along the way now at this point my giant
08:24
module is actually completely empty and
08:25
it's just a shin in front of the other
08:27
modules you can keep iterating and
08:29
tweaking things to to your heart's
08:30
content and then maybe at the end of
08:32
this you're finally able to just drop my
08:34
module or my giant module entirely this
08:37
part is a breaking change but you've
08:39
basically done all of the attorney
08:42
changes under the hood well before you
08:44
got there so this you can just do in one
08:46
atomic change and you're done
08:48
if you want you can even also give
08:50
consumers to this that you're working on
08:52
like a library team or platform team
08:54
where other people are using this and
08:56
you want to let them migrate at their
08:58
own cadence you can just deprecated it
09:00
and then you have all the replacement
09:03
pieces ready for them to use even better
09:07
though if you're using constructor
09:08
injection you don't need any of this in
09:11
fact if you're doing constructor
09:12
injection with these two then all you
09:14
need to do for your modules is that you
09:19
don't have to write any modules for this
09:21
because dagger can just construct them
09:22
for you this brings another interesting
09:24
point for module is a ssin though that's
09:26
worth mentioning what if these are
09:28
separate projects so say when we compile
09:32
feature a here if we have no prior code
09:34
gen done then dagger is gonna generate a
09:36
taco factory class this is what dagger
09:39
uses in Co gen to constructed and linked
09:42
dependencies into it now if we use this
09:44
didn't feature be the same thing is
09:46
going to happen now if I'm consuming
09:47
both of these features in my apps then
09:49
we're down the line what's going to
09:50
happen here is we're gonna actually have
09:51
a class path conflict between these two
09:56
because dagger compiled or generated
09:58
both of them you're trying to use them
09:59
both later and the compiler is going to
10:02
look at you and be like something's a
10:03
bit weird here so how can we solve this
10:05
and the solution here is pretty simple
10:07
you just run the dagger compiler over
10:09
the food sub project as well where this
10:10
lives and dagger will just generate the
10:12
factory there instead now in later
10:14
versions so in feature a and feature B
10:16
dagger will actually recognize that this
10:18
class was already generated and just
10:19
reused the same instance for everyone
10:22
let's cool on to the next tip let's talk
10:24
about internal and private api's
10:26
this one is pretty quick so let's look
10:28
back at that food module right now
10:30
anyone who wants to include this module
10:32
are going to get seasoning as a
10:33
dependency if they wanted to as well but
10:35
today I wanted to make seasoning a
10:37
private implementation detail of the
10:39
taco but I still want to use a separate
10:42
provider so we can use qualifiers in
10:45
dagger and compiler to do this for us
10:48
now at this time when I say compiler I
10:50
mean the actual Java compiler or Calvin
10:52
compiler so writing qualifier is easy
10:54
I'm just call it internal API now to use
10:58
this the trick here is to just make it
10:60
private
11:01
so I'm caught then you can just put this
11:03
in the same file as the top-level class
11:05
that you're using it in in Java you can
11:07
just put this as a inner class of the
11:10
module that you're using it in and then
11:12
you just take this annotation drop it in
11:14
on the dependency you want to make
11:17
private and now boom like seasoning is
11:20
no longer accessible outside of this
11:22
module because no one outside of this
11:25
class can actually even use that
11:26
annotation so they can't ask for it even
11:28
if they want to and so that's a sort of
11:31
roundabout way of letting the compiler
11:33
do work for you and guarding your your
11:36
dependencies the last area that I want
11:40
to cover is multi bindings so these are
11:42
all sensibly the coolest and most
11:43
powerful tools here and they're also
11:45
very open-ended into what you can do
11:46
with them they're pretty standard in D I
11:48
so of Jews dagger 1 dagger 2 all had
11:50
them and the core components to them are
11:53
maps and sets so a quick refresher set
11:57
is corresponding the annotations like
11:59
adding to said adding elements in the
12:00
set map is corresponding to things like
12:03
add into map and add map key you can
12:05
declare it upfront and basically just
12:07
collect multiple instances of a given
12:09
type that you can then use in bulk later
12:11
so let's look at our food module again
12:13
the same as before we have seasoning in
12:15
a taco but what if our taco accepts
12:16
multiple seasonings as most tacos should
12:18
we have a couple of types here so we've
12:21
got spicy seasoning although passive
12:22
seasoning but we've lost a little bit of
12:25
our di principle here though the talk of
12:27
provider at the bottom here knows quite
12:29
a bit about the seasonings coming into
12:31
it and this is where multi bindings will
12:34
really shine we can just accept a set
12:36
and provide into a set so we declare the
12:40
setup here and then we just add annotate
12:45
these providers with add in to set and
12:47
then at the bottom here we just accept a
12:50
set of seasonings and daggers canape so
12:51
all this information together this also
12:54
allows other modules to contribute into
12:56
a set so if I want to pull these
12:57
seasonings out to another module this
12:59
will still work fine and this is where
13:02
things become really powerful anywhere
13:04
where you're contributing multiple
13:05
instances of something can leverage this
13:07
kind of
13:08
tool so this is a trivial case but say
13:12
real world cases where you have
13:13
pluggable api's so ok GDP and it's
13:17
interceptors or maaske and json adapters
13:19
or timber and logging trees or more
13:22
advanced cases you could gate entire
13:25
features behind this in a DI friendly
13:26
way so let's make this a map and yeah
13:32
this in our screen here we've got
13:38
different categories for pets we've got
13:40
dogs and cats and birds I'm not totally
13:42
convinced that cats count as pets
13:43
because they're more like little tigers
13:44
in your house that say you want to wrap
13:50
them all up in this sort of category
13:51
based API now what if you want to make
13:53
them lazily initialized well in dagger
13:55
you can just wrap it up in lazy and
13:57
dagger we'll just natively understand
13:59
this and wrap them all and lazy for you
14:00
you can also do them with a provider to
14:03
guarantee new instances and no other
14:05
changes are required dagger is gonna do
14:07
all this for you under the hood it's
14:08
zero overhead to do this zero sourced
14:12
overhead so you could even implement an
14:16
entire app behind a plug-in system
14:17
backed by this so this is the I project
14:20
app of mine on github called ketchup and
14:22
each of those tabs is actually added in
14:24
a multi binding so this main pager
14:26
actually knows nothing about these
14:28
individual services it just gets a
14:30
mapped multi binding coming into it and
14:32
then unwraps those services and creates
14:34
the new instances for each of these tabs
14:37
in these maps you can just have keys at
14:40
it in here as metadata for the service
14:42
or like the ID some of these bar bits or
14:44
a trivial but what if you wanted to say
14:46
do experimentation flagging it's
14:48
actually really easy with this you add
14:50
this say experimentation ID and then
14:52
just build throughout your injection
14:54
site for whichever ones are enabled and
14:56
then you drop the ones that aren't ready
14:57
yet and that's it
15:02
these are some of the examples that
15:04
inspired a lot of this this blog post
15:08
here is a longer write-up of that
15:11
plug-in system that I was just
15:12
describing with my side project app
15:15
and yeah I hope you learned something
15:18
and if there's any questions I'll be
15:19
hanging out here on the side after
15:20
Thanks
15:23
[Applause]
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One