[MUSIC PLAYING] JAKE WHARTON: Hi, everyone. My name is Jake. I work on the Android
team on Kotlin stuff. And so today, I'm going to
be talking about Android KTX. And I'm not going
to be just going over a bunch of the
stuff that's in there. I want to make it a little
more interesting than that. So I'm going to start with a
little bit of what happened last year at Google I/O. I was here last year,
talking about how you can write extensions
for Android types, such as this example,
where we have code that iterates over the
views inside of a ViewGroup. You can pull that common
code out into an extension. And what this extension
does is enhance a type that we don't control,
the ViewGroup type. We're allowed to, essentially,
create a member function that's not actually
a member function. It actually turns into a static
function in the bytecode, with the functionality
that we want to enhance. And so we can take
our original code that had the explicit for-loop in
it and use this new member to create a more concise version
of what we intended to do. It is actually
visually distinguished from a normal member function,
that it's italicized. If you use dark yellow,
it'll actually be yellow. But it's semantically
equivalent to calling a member, or the intent is to feel
semantically equivalent. And so oftentimes,
when you start talking about
extension functions, you think, well, if
this is so useful, why don't we just put the
function directly on D-group? Why doesn't ViewGroup just offer
a for-each and a for-each index that takes in a lambda? And really, the reason
is because of the lambda. When we pass a lambda in Java
8 or in Kotlin, by default, that has to create an anonymous
class, which eats up methods and causes class-loading. Kotlin, however, provides
language functionality, which allows us to eliminate
that lambda's allocation. By marking the
function as inline, the body of the extension gets
copied into the call-site, and we have a zero
overhead abstraction. Let's take a look
at another example. In API 23, we were able
to get a system service based on the class type. And in 27.1 of the
support libraries, a ContextCompat
version of this was added that allowed it to
work on all API levels. We can pull this into an
extension that is also inline, like the previous one, but
doesn't contain a lambda. What this one has
that's different is something called reified. Now, this is a compiler trick. And what that trick does is
it forces the type information of the generic to be
known at compile time, so that it can be made
available at runtime. And so this is
what allows us to-- where we would otherwise
be calling class.java on notification manager, we
can now abstract that away behind this extension. And so our calling code now
becomes simplified to just be able to pass the generic. And because it's reified,
the implementation of that has access to be able
to call class.java. So if we want to update
the padding of a view, just where we're only
specifying two of the four parameters-- in
this case, we want to update both the
left and the right. We have to pull out
the existing padding for the top and the bottom
because Android requires you to specify all four. This is something that
we can remedy, again, using an extension function. The key here is that,
for each of the arguments on this new function
that we've defined, we're specifying a
default. And that default will be used when a value is
not provided for that argument. So it allows us to take
the calling code where we're specifying all four and,
now, specify them as just two. But the problem here is
that we've eliminated two of the arguments. But since we're
only supplying two, Kotlin takes that as
meaning the first two, and the latter two are the ones
where the defaults are used. This is not what we intended. We intended to do
left and right, which are the first and third. Another language feature
comes to help here, which is named parameters. By specifying the
name of the parameter, we're able to tell the compiler
which of the two arguments we're specifying,
and allow it to fill in the defaults for the others. OK. Android APIs have a
bunch of composite types. These are things like
point, rectangle, pair-- even the location class. These composite types
are just wrappers around smaller individual
pieces of data. In this case, I'm calling an
API, which has a rectangle, which is a composite around the
four-- the left, top, right, and bottom-- values of a rectangle. And if you need
to do calculations based on the values inside
of these composite types, you have to pull them out into
individual values or variables in order to do that calculation
and then, potentially, put them all back together. So with the help of the
extension, we can avoid this. This one is a little
bit different. We have a new keyword
called an operator. An operator means
that Kotlin will allow us to use a
special call site syntax. And each operator function
has a very specific name-- a well-known name. You can't just make up any name. And the name defines which
call site syntax that you're intending to create. In this case, it's
called Component. And Component allows
us to use a feature of Kotlin called destructuring. And so our original code, which
had to individually pull out the four different components,
can now use this call site syntax where the rectangle
was automatically unpacked into the four values
and assigned to four variables with the names that we choose. What's really nice
about this is that, if you don't care about ones
later on, you can omit them. And if you don't care
about ones in the middle, you can specify
them as underscore. And so, if we just need to
pull out two of the values, we can do that very succinctly. OK. An experienced Kotlin
user might know that-- well, I guess
we flipped through-- could go back a slide? OK. So this is some
code that shows how we could determine
whether or not a string contains only digits. It basically just loops
through the characters, using Kotlin's for-in syntax;
checks whether it's a digit, using an extension
function on character; and then sets a value
to true or false whenever it detects a non-digit. If you're an
experienced Kotlin user, you might know about
the All function, which exists on string, which
encapsulates the same looping and allows you to specify a
predicate, which, in this case, is digit. It's actually an
inline function, so it desugars into the exact
same thing we would have wrote in the previous slide. But what's interesting is
that Android actually has a built-in function for this. And I suspect that
a lot of people don't actually know this exists. And so this is something that
we can actually take and turn into an extension. But you start to wonder,
is this actually worth its weight in an extension? What value do we gain by turning
this static method that we can call into an extension? Well, for one, it
changes the way that we invoke to feel a lot
more natural and idiomatic in Kotlin-- sure. But still, is there really
value that we extract from this? The biggest one that I
think we gain from this is that, when you're in the
IDE, you have your string, and you're wanting to make
this query as to whether or not it contains only digits. If you didn't know that static
method on TextUtils was there, you probably would
never find it. When it's extension, if you
start typing in the IDE, it will actually show this
extension and auto-complete, where it's much more
discoverable than otherwise. So you just press Enter. And you get it. All right. So I covered a few
extensions here. I just wanted to
remind you a bit of the power of these
extensions, the fact that we are leveraging language
features that exist only in Kotlin, not in
the Java language. And actually, some
of these examples we're going to keep coming
back to throughout the rest of this talk. So all of the extensions
that I just showed are part of the
Android KTX library that we announced
in early February. There's been two
releases since then. And as of Tuesday, it's
now part of Jetpack, and versioned with Jetpack. So on Tuesday, Core-KTX
is now 1.0 Alpha 1. It's going to be
versioned and released with future Jetpack libraries. So we called this
Core-KTX when we launched, which was kind of a weird name. It didn't make sense. This was for extensions for
types only in the framework. A lot of people suggested, can
we add support library stuff? And we were very
adamant about saying no. That should, hopefully,
make a lot more sense now. But even this
isn't exactly true, because Core-KTX initially
depended on support-compat. Support-compat is there to
provide backwards compatibility versions of things that are
in the Android framework. And so earlier, I showed the
example with ContextCompat. That's something that
came from support-compat. And so now, with the Jetpack
rebranding and the Android X packages, support-compat
has become core. And so now, Core-KTX
lines up with Core. So we kind of knew
what we were doing back when we started this. And now, it's only
starting to pay off. Along with the other
Jetpack libraries, there's actually a
few new KTX libraries that are launching with it. So we have ones for
fragment, collection, SQLite. For the newer components,
navigation, and work runtime. I'm going to touch on how you
can discover these a bit later. But I want to talk
a bit about scoping, about how we determine whether
or not something should go into one of these libraries. All right. So in Core-KTX 0.3, we
offered an extension that looked like this. If you look at its
signature, it's an operator. And it operates on color. And the name is Plus. So this allows us to use the
normal plus syntax for adding, for compositing two
colors together. So the signature-- it's
definitely an extension. But the body of this
looks very different than the other
extensions we looked at. There's a significant
amount of code in here. If you look inside,
support-compat, which is now Core, there
is a color utilities class. And that color
utilities class has a method called composite colors
that works on integer colors. It allows you to take a
foreground and a background and turn them into
a single color. So this is the perfect candidate
for placing the implementation of what that
extension function was into this class, so that
everyone can use it-- so that can be used
from the Java language or the Kotlin language. And so in Core-KTX
1.0, this actually has been rewritten to just
delegate to that ColorUtils. So the Java language users
get that functionality. But the Kotlin users
get the enhanced syntax. And if you look at the
extensions that we talked about so far, the bodies of
them, the implementation of these functions,
they're all trivial. They're exceedingly trivial. And that's by design. And this gets me into covering
some of the principles that we defined that KTX
extensions should have. And so this first one is that
we want to adapt functionality that already exists. And if we want to
add any new features, those should be redirected
upstream to a place where they're language agnostic,
where both languages can take advantage of them. Other examples of this-- there was some HTML compat
stuff and a path iterator that were implemented first
in Core-KTX that have since moved upstream into
Core to be able to be used in both languages. Another thing that's common
to all these extensions is that they're
marked as inline. The reason that we do
inline on the first one-- the one at the top-- is that we want to avoid
the lambda allocation. For the second one, because
we're using reified generics, we're actually forced to
use inline by the compiler. The third, the component
ones, and the very bottom one are all inline,
mostly because they're just aliases to what you
would otherwise write if the extension didn't exist. If we look at an example
of something that's not inline in Core-KTX, we
have this iterator extension to ViewGroup, which allows
us to use Kotlin's for/in syntax to iterate over
the views and a ViewGroup. This is not inline for
a very specific reason. And that is because the
implementation of this function defines an anonymous class. If we were to inline this,
that means that every time you use it, an anonymous class would
be defined at your call site. And so this would increase
your deck size, method count, and class loading. We explicitly make
this not inline, because we want that
single implementation to be reused by all of the callers. So we default to an extension
being inline unless there are allocation reasons. And I should note
that this is really only for KTX-style extensions. In normal Kotlin code, this
is not a good recommendation. You don't want to
default to inline because it has the potential
to lead to actually having a negative effect on your code
rather than a positive one. All right. So earlier when we
showed this extension, I talked about how the inline
modifier, coupled with the fact that there's a lambda,
allows this extension to be a zero
overhead abstraction. In the reified case,
we get the ability to have a more
declarative version of the lookup at the call
site without having to specify the colon colon class.java. For updating the padding,
we get to use default values to not have to specify
each of the arguments, and name parameters to specify
which subset of arguments we want to actually provide. For the destructuring case,
we get the fancy syntax that allows us to pull apart
the component variables out of a composite object. This is useful. This is enabled by
the fact that we have operator overloading in Kotlin. We also talked about how we were
able to add the plus for color. For this one, we're aliasing an
extension to a static method. And this is just to help
improve discoverability for built-in helpers that you
might otherwise not know exist. And then, for types that
are collection-like but not actually collections,
we have the ability to turn them into pseudo
collections, where we can use the affordances
of the language as if they were
actual collections. And so each one of those has a
very Kotlin-specific language feature that it uses. And we want to make sure that
all these extensions that we're defining leverage some
feature of the Kotlin language that doesn't otherwise
exist for Java callers. We want to resist trying to
fix an API just by creating extensions for it,
but rather enhance it to become more pleasant
to use by leveraging these Kotlin-specific features. OK. One of the suggestions
we get quite frequently is to take something
like setOnClickListener and write an
extension, which allows you to call it using something
like Click or OnClick. This allows the calling code-- instead of having to
call setOnClickListener, we get the shorter
version of Click. Are we leveraging a feature
of the language here? Well, we're leveraging extension
functions, but not really. We're really just
creating a shorter alias. What value are we extracting
from this extension? Well, we're typing a
few less characters. But really, it's
autocompleted anyway. But even worse, what precedent
will we be setting here by adding this extension? Are we going to do this
for every listener? And so this is a great
example of something we explicitly do not want
to do in the KTX libraries. If you're not familiar
with the term, we call this code
golf, where you have the desire to create
the shortest code possible. This is something we
do not want to do. We're not here to just
make the code shorter. OK. There's another one that gets
suggested every now and then, and that I've seen people using. With Android, because of
the different API levels we have to support,
you very frequently see these IF checks
around the SDK int. So it can be tempted
to pull this out into an extension,
where you have a little bit more
declarative version of this. We move the comparison
into an extension function. It's an inline function, so
we don't have the overhead. The lambda's the last parameter,
so we get the nice Kotlin call site syntax. And it turns our IF statement
from this into this. Now, this by itself
is not too terrible. We're really not leveraging
any of the language features. Again, similar to the last one,
it's still kind of an alias. But at least this one, you
can argue a little bit more for its merits. But there's a problem. While these two statements are
equivalent, what happens when-- oh, one thing is that you can
at least static import SDK int, and then they're a
little bit closer. So that's one reason why
this is less justified. But one thing is
that an IF statement is a very primitive construct
of a programming language. And because an IF statement
is not just an IF statement, there's constructs like ELSE. So what if your
requirements change such that you need to alter
the behavior on these two different versions? Well, if you were using this
extension that you wrote, in order to support
this case, you either have to change back to
using an IF statement or you have to
modify the function, where maybe it takes
two lambdas now. One for the case where you're
above 19, one for the case where you're not. Because we're not taking two
lambdas in this function, we've lost the special
trailing lambda syntax, where we now have to pass
them as arguments inside the parentheses, whereas
before, we didn't. So immediately, this extension
starts falling apart. If we introduce another
conditional branch-- maybe we need to vary
the behavior across APIs in three different ways-- well, there's really
no way that we can make the extension do this. The other thing that is
different about this extension compared to the IF statement
is that we're assuming the conditional that
we want to check is greater than or equal to. That the behavior we want
to run in the lambda, we only want to run on 19-plus. Well, a lot of times, some
of the IF statements-- again, SDK int-- will be
less than or equal to. And so now, we need a
second extension in order to support that use case. So this is another
example of something that we're not looking to do. We don't want to optimize
for just a single use case or a specific use case,
where the extension only supports one way
of doing something. And then, when you need to
move to something more complex, you have to revert to
the original behavior. We want the extensions
to allow you to express everything
you would need to express if it didn't exist. OK. So all the extensions we've
been talking about thus far have been ones that are
in the Core-KTX library. I don't want to go through
a ton of the extensions that are in these other libraries. Again, I'm going to show you how
you can discover them in a bit. But I want to touch on one. So for the fragment KTX,
we have an extension, which encapsulates transactions. We move the beginTransaction
and the commit function calls into an extension. We use the fact that we
can use an inline function and a lambda, again, to turn
this into a zero overhead thing. Our calling code then
becomes a little bit shorter, where we now use the
transaction with a lambda body. So if you've used
fragments, you'll know that commit is not
the only commit function. There's actually more than one. And so we can model
this by doing something like allowing you to
supply a Boolean as to whether or not you want to allow
state loss or disallow state loss when you're committing. This is really easy
to accommodate. But it sort of goes
against something I said earlier, where-- oh, and we can update our call
site to be able to use this. It goes against something I
said earlier though, where I talked about minimizing the
impact of the implementation of these extensions. Since this is an
inline function, and then we've now put a
conditional inside that inline function, that conditional
is being inlined into all of the call sites. And so all of the
call sites now have to have that conditional
inside of them. So is this actually a bad thing? Well, if we look at
the bytecode that gets generated
from the call site, when we specify allow
state loss true-- you don't really
have to understand bytecode to understand
what's going on here. There's essentially
three function calls. The first one is
beginTransaction. The second one is that replace,
which was inside the lambda. And the third one is just a
call to commitAllowingStateLoss. There's no IF statement here. There's no conditional. And that's because, since
this is an inline function and since the
argument is a Boolean, the compiler actually
knows, at compile time, what value you're supplying. And so since it knows
at compile time, it can actually do
dead code elimination and eliminate the branches that
can never possibly be executed. And so you actually
get, in bytecode, what's equivalent to what you
otherwise would have written. And there's actually
more commit functions. There's one which allow you
to commit now and commit. So we can also support that by
adding an additional Boolean. And the same thing happens here. Even though they're now
nested, dead code elimination will make it so that
there is only one function call in the resulting bytecode. OK. As part of this effort of
all these releases at I/O, one of the things
that we've done is start creating
a Kotlin-specific view of the libraries that
we publish in the Android framework itself. So if you see, in
that blue box there, when you visit the
reference docs, it'll actually ask
you if you want to view a Kotlin-specific
version of the platform or Android X libraries. And also, if you scroll down
in that left navigation pane, at the very bottom, we have
links to them, as well. And what these are are a
Kotlin view of these libraries. And so when you're browsing
through, say, the fragment package, you'll be able to see
the extensions for fragment inside the documentation. It's no longer
completely separate. One thing that's
missing right now is that we don't
actually tell you the maven coordinates of the
artifact that these come from. That's coming soon. And also, the
extensions in Core-KTX, which extend the
platform types, don't yet show up on the platform docs. But this is something
that we wanted to get out to show you
that it's being worked on. And so hopefully, those two
things will be coming soon. All right. I'm here to talk
about Android KTX. But Kotlin extensions--
there's nothing Android-specific about it. What we're doing is
building extensions to try and make these
libraries more Kotlin-friendly. And that's something
that any library can do. And so I want to
talk about the ways that we think about how
we can make libraries more Kotlin-friendly that apply to
both the Android libraries, but also apply to libraries
that you might be writing or you might be using. The first way to make a
library really Kotlin-friendly is just rewrite the
whole thing in Kotlin. Obviously, this isn't
feasible for every library, but it's certainly
an option for some. If it's a library that's
private to your app-- it's in your repository or
it's internal to your company-- and you're already using
Kotlin, this is a viable option. It doesn't seem like something
that's totally viable for, say, the Android framework. And I'm not quite sure we're
at the stage where an Android X library could do this. Maybe a future Android X library
could be written in Kotlin. That seems like a
strong possibility. What we've chosen to do
with most of the things that we publish is
sibling artifacts. So the main library
remains written using the Java language. And we ship Kotlin language
features as a sibling artifact. What's great about
this is you don't force the Kotlin standard
library onto your consumers unless they explicitly want it. You can curate the
extensions to be exactly what's needed
to augment your API, where you get the
Kotlin-specific features. And what's really
nice about this is you don't have to control the
library that you're extending. So if you're just
consuming a library and you want to make part
of it more Kotlin-friendly, you can do that. You can do that
either in your own app or you can publish a set
of extensions for a library that someone else publishes. But are these the
only two options? I want to take a
look at something that I think will lead into a
third somewhat hybrid option. And I go back to this simple
alias extension, where we've taken the static method
defined in the Java language and turned it into an extension
method in the Kotlin language. If we look at the implementation
of this class on the Java side, I've included the
first line, because we can see that it
immediately dereferences the argument that we pass in. As soon as we pass
in a string, it says, what's the maximum
number of characters that I can iterate
over, in order to determine whether or
not there are digits? And so if you've been using
Kotlin with Java APIs, you might know that this
means that the parameter is going to be exposed as what's
called a platform type. It has unknown nullability. But from the implementation,
we know right away that this method simply cannot
accept null values. And the way that
we would fix this is by adding the
non-null annotation. So what this annotation does is
it informs the Kotlin compiler that there is a restriction. That there is special
behavior that it needs to take into
account, where it needs to enforce
that no one passes a potentially nullable value,
or null, into this method. And so this is enabling a
language feature in Kotlin that simply doesn't exist in Java. Now, you can use tools that
will allow this enforcement to work for Java. But it's not intrinsic
to the language itself. So if we can do something
like that for nullness-- if we can add this
annotation for nullness to inform the Kotlin
compiler that it needs to change its behavior
when we invoke this method, can we do this for
something else? Say, I want to take this static
method where the first argument is really the receiver. And can I say that
this is actually going to be an
extension function when invoked from Kotlin? And what this allows
us to do, potentially, is eliminate the need to
have this explicitly-defined extension at all. This extension only exists to
change the calling convention, to inform the compiler that we
want to allow you to call it in a different way. And so now, we're
left with just this. The Kotlin compiler
sees that annotation, just like it saw the
non-null annotation, infers something
from it, and allows you to call it in
a way that's more idiomatic for that language. In the bytecode, we get what we
otherwise would have written. We still get the call
to the static method, and the receiver becomes
the first argument. How about this example? One thing you might
have noticed is that this extension is named
updatePadding, not setPadding. Now, we can actually call
this extension setPadding. But the problem is
that it will only work for a subset of arguments. So in this case,
where we're just passing left and right values,
we could call that setPadding, and it would work fine. But if we passed left-right
and then top-bottom, we'd be supplying
four arguments. And the Kotlin compiler
is going to see that the real set padding
also accepts four arguments. And it's going to prefer
calling the real one. And the real one doesn't
have named parameters. So you're going to get
a compilation error. That's the reason we have
to name this updatePadding. If we look at the
real setPadding-- a simple method that
takes four integers-- what if we could inform
the Kotlin compiler that these parameters have
names associated with them? Now, it'd be nice to infer this
just from the parameter names directly and not have to
specify the redundancy. But I'll argue
that, for one, it's very nice being explicit about
these names in the annotation. In Java 8 bytecode,
there actually is a way for you to
retain parameter names. So the Kotlin compiler
could, in theory, use those. But one problem is
that then it becomes an all-or-nothing thing. You have to opt in
to this behavior. And then suddenly,
every parameter name across your library
is set in stone. Whereas, with
annotations, that's something that you could
incrementally migrate. So this has the potential to
solve the naming part where, now, we can call the
real method from Kotlin and specify the four arguments
in any order that we want, based on what names we provide. How about the default value? What if we could specify
a Kotlin expression, which allowed the compiler
to supply a default when one wasn't supplied by you? This would change our original
extension calling convention from calling our extension
to actually just using the real method. And then, in the
bytecode, we get the thing that we started with, the thing
that our explicit extension would inline to. But now, the extension
doesn't have to exist. The metadata that we added
in the form of annotations informed the Kotlin
compiler that we wanted to enhance
our ability to call this function in a
Kotlin-specific way, leveraging the Kotlin features. And so we're able to do so. So Kotlin has this process,
which is called KEEP. It's Kotlin Evolution
and Enhancement Process. And just this morning, we
proposed these annotations as KEEP-110. So this is something
we're proposing to add to the Kotlin
compiler, so that it can understand these annotations. We have ExtensionFunction
and ExtensionProperty, which are for static
methods; DefaultValue, which allow supply and
default values for parameters; and then KtName,
which allows you to provide an alternate name for
methods, fields, or parameters. Now, it's very important to note
that this is extremely early. These names might change. The semantics might change. This may never
actually be accepted into the Kotlin compiler. We have been working
with the JetBrains team for quite a while on this. And some of this is
already prototyped inside the Kotlin compiler. We really think this would be
a way that we could enhance the Android framework
for Kotlin callers, without actually having to
go and rewrite the Android framework, or at least its API,
in Kotlin, which is really not feasible. And it's also important to note
that, while this is an option-- assuming that it actually makes
it into the Kotlin compiler-- it doesn't totally
solve every problem that our existing
extensions are solving. We determined these annotations
that we proposed in KEEP-110 through looking through a
bunch of open source libraries, looking through
our own libraries, and seeing what we thought would
be the most useful extensions-- what the pattern of Java methods
were, such that they would want to be turned into extensions. And so the latter two
really are complementary. The big advantages
of the annotations is that you retain the
single source of truth. You don't have to
really know Kotlin. You don't have to add Kotlin
compiler to your build system. You don't have to publish
sibling artifacts. Even if you're a
pure Java library, you can add these annotations
and just enhance your API, so that Kotlin callers get
the more idiomatic syntax. All right. So to sum up, Core-KTX is
now part of Android Jetpack-- versioned with Android Jetpack,
released with Android Jetpack. There's a few new artifacts, as
you can see here on the screen. There's definitely more coming. Notable ones that
we think are missing are slices and view model. So I would not be
surprised to see artifacts for those in the coming months. Please check out
the Kotlin version of the reference document. This is extremely early. This required
changes in [? doca ?] and how we produce docs. And so it's something
that we just wanted to get out there
and show you as a preview. This is definitely
something that's being actively worked on. There's a new component
on the Android bug tracker for Android KTX. Because Core-KTX and
all the KTX libraries are now a part of Jetpack,
the source of truth has moved into the Android
Support Repository. We're going to be migrating
the GitHub issues on the GitHub project over to this bug
tracker in the coming weeks. But it's important to
note that we're still going to be accepting pull
requests to the GitHub repo and syncing things back
out to the GitHub repo. It's just that the
issues will no longer be the source of truth on GitHub. It will be on the
Android bug tracker. The [? KEEP ?] was proposed-- I created the pull
requests about an hour ago. Please go check that out. The document contains a lot
more detail about examples. And like I said, the
annotations that were chosen were the ones that we
think have the most impact. But at the bottom
of the document, you'll see that, if something
like this gets accepted, there's a potential for future
enhancement of even more. The link to that should be this. I made this link last night
before I submitted it. So hopefully, it's accurate. And that's it. Thank you. [APPLAUSE] [MUSIC PLAYING]
Great talk, u/JakeWharton!
Don't let the boys at r/mAndroidDev see this
Woah I never thought about adding extension operator overloads for
componentN
to turn something into "data class like", that's cool!For people who want
button.click {
, you can addAnd you'll have a
view.onClick {
.This
@ExtensionFunction
KEEP sounds really cool, and simple compared to typeclasses that is for sure :DI am most curious is how Google is going to get from Android to Fuchsia and if things like JetPack are being created with an eye towards that future?
We now have an ART branch on the Fuchsia code tree. So looks like Android will sit on top as a first class citizen and then also Flutter. What is the plan on bringing this all together over the next couple of years?
Then the biggest question. Will Fuchsia finally force OEMs to update their damn phones? Now have a Pixel 2 XL so no longer have to worry about it but for everyone else.
That troegs
Wharton bless