About the talk
This talk introduces Android KTX, which is the new set of Kotlin extensions for Android. It demonstrates how Android developers writing Kotlin can use Android KTX to make their code more idiomatic, concise, and pleasant. It will also discuss how developers can make their own libraries more Kotlin-friendly.
12:58 KTX principles
17:50 Click listener
22:30 Android KTX
26:54 Building Kotlin-friendly libraries
31:26 Extension function
Hi everyone. My name is Jake. I work on the Android team on kotlin stuff. So it's not going to be talking about Android KTX and I'm not going to be talking about 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 Google IO. I was here last year talking about how you can write extensions for Android types such as this example where we have code that iterate over the views inside of a few group. You can pull that common code out
into an extension with this extension does is enhance a type that we don't control The View group type. We're allowed to essentially create a member function. That's not actually a member function that actually turns into a static function in the bike code 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 its italicized if you use dark yellow to actually be yellow but it's semantically equivalent to calling a number or the intent is to feel semantically And so often times 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 View group? Why doesn't View group just offer ofori channel for each index that takes an a Lambda. And really the reason is because of the Lambda when we pass the Lambda in Java 8 or in kotlin by default
that has to create an anonymous class which heats up methods and causes class loading. Colin however, provides language functionality which allows us to eliminate that lamb does allocation by marking. The function is in line the body of the extension gets copied into the call site, and we have a zero overhead abstraction. What's a good look at another example in Epi 23 word able to get a system service based on the class type and in 27. One of these support libraries contact Kim pad version of this was added that allowed it to work on all ATI levels.
We can pull this into an extension that is also in line like the previous one but does not 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. So this is what allows us to take where we would otherwise be calling you no class. Java on notification manager. We can all abstract that away behind this expansion into our calling code now
becomes simplify to just be able to pass the generic and because it's reified we have the implementation of that has access to be able to call class. Java. So if you want to update the padding of a view just where were 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 or specifying at the fault and not the fault will be used weather when value is not provided for that argument. solasta take the calling code where were specifying all four and now specify then as just to But the problem here is that you know, we've eliminated two of the arguments. But since we're only supplying to kotlin takes that as meaning the first two and the the latter two are the ones who are the defaults are used. This is not what we
intended me intended to do left and right which of the first and third another language feature comes to help here. Which is named arguments by supplying the name of the perimeter or able to tell the compiler which of the two arguments were specifying and allow it to fill in the defaults for the others? Okay, Android has Android if you guys have a bunch of composite types, these are things like Point rectangle pair even the location class is composite sites or just rappers around smaller individual pieces of data. In this case. I'm calling an API which has a rectangle which is a
composite around before 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 let's actually put them all back together. So with the help of the extension we can avoid this. This one is a little bit different. We've been new keyword called an operator and operator means that Colin will allow us to use a special call sites
in tax in each operator function has a very specific name a well-known native. You can't just make up any name and the name defines which call sites in tax that you're intending to create in. This case is called component and component allows us to use a feature of Scotland called restructuring. And so our original code which has it individually pull out the four different components and now use the skull sites. In fact, we're the rectangle is automatically unpacked into the four values and assign 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 to the values we can do that very succinctly. Okay, an experience calling user might know that well, I guess we use flip that we go back inside. Okay. So this is some code that shows how we can determine whether or not a string contains only digits it basically just Loops through the
characters using Collins for instant tax checks, whether it's a digit using an extension function on character and then set the volume to two or false whether it's a text whenever it's a Texan on digit. If your experience calling user you might know about the all function which exists on Strang which encapsulated the steam looping allows you to specify a predicate which in this case has the is digit It's actually an inline function. So it it's D sugars 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 what 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 for one changes the way that we invoke to feel a lot more natural an idiomatic and cotton sure but still is there really value that we extract from this? The biggest one that I think we came from this is that when you're in the IDE,
you have your string in your wanting to go into Turman whether or not it's you wanting to make this query as to whether or not it contains digits. If you didn't know that static method on text utils 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 in autocomplete where it's much more discoverable then otherwise, so you just press on her and you get it. All right, so I covered a few extensions here. I just wanted to remind you in 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 were going to keep coming back to you throughout the rest of this talk. So all 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 today, it's not part of Jetpack part inversions with Jeff akzo on Tuesday for ktxs is now 1. L Alpha One It's going to be version and release by
Future jetpack libraries. I'm so we called this court case he acts when we launched which was kind of a weird name. It didn't make sense. This was for extensions for only in the framework. A lot of people suggested support Library stopped and we were very adamant about staying know that should hopefully make a lot more sense now, but even this isn't exactly true because cork ATX initially depended on support support kempot is there to provide backwards compatibility versions of things that are in the Android free market until
earlier. I showed the example with contacts compat. That's something that came from supportive that and so now the jetpack rebranding and the Android access packages. Sport compact 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 as we have ones for a fragment collection sequel light for the newer components navigation and work run time.
I'm going to touch on how you can discover the use of it later, but I want to talk a bit about scoping about how we determine whether or not something is something should go into one of these libraries. All right, so in court Katy TX 0.3, we offered an extension that looks like this if you look at it signature, it's an operator that it operates on color and the name is + so this allows us to use the normal plus Syntax for adding for compositing two colors together
to 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 a sport camp at which now poor there is a color utilities class and that color utilities class has a method called composite colors that works on inner door colors allows you to take a foreground in the background and turn them into a single color. This is the perfect candidate for placing the implementation of wear that it what that extension function was
into this class so that everyone can use it so I can use from java language or the column language. And so on Corky tx1. Oh, this actually has been Rewritten to just delegate to that color utils. So the Java language users get that functionality, but the Colin 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 are all trivial. They're exceedingly trivial and that's by
Design and this gets me into covering some of the principles that we wanted it that we Define that KTX extension should have So the first one is that we want to adapt functionality that already exists. And if we want to add any new features, though should be redirected Upstream to a place where their language agnostic we're both languages can take advantage of them. Other examples of this was there was some HTML compact stuff and a path iterator that were implemented first and Corky TX that has since moved Upstream in too poor to be able to use them
both languages. Another thing that's comments all these extensions. Is that there Marcus in line? The reason that we do in line on the first one, the one at the top is that we want to avoid the land allocation. For the second one because we're using reified generics. We're actually forced to use in line by the compiler. The third the component ones and the very bottom line are all in line mostly because they're just aliases to what you would otherwise right if the extensions didn't exist.
If we look at an example of something that's not in line in Cork ATX. We have this interrater extensions if you group which allows us to use Collins for in syntax a generative interviews in a few group. This is not in line for a very specific reason and that is because it defines 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 will be to find out your call sight. And so this would increase your dick size method count and class clothing. We explicitly make this not in line
because we want that single implementation to be reused by all the colors. So we default to an extension being in line unless there are allocation reasons and I should know that this is really only for GPX file extension in normal collincote. This is not a good recommendation. You don't want to default the in line 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 show 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 reifier case we get the ability to have a more declarative version of the look up at the call site without having to specify the colon colon class. Java. For updating the padding we get to use default values do not have to specify each of the arguments and named parameters to specify which subset of arguments. We want to actually provide. For the destruction in 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 are we have operator overloading kotlin. We also talked about how we were able to add the Plus or color. After this one, we're aliasing and 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 four 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. It so each one of those has a very Colin specific language feature that it uses and we want to make sure that all of these extensions that we're defining leverage some feature of the kotlin language that doesn't otherwise exist for Java. Collars. 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 you Scotland specific features.
Okay. One of the suggestions we get. Quite frequently is to take something like that onclicklistener. And Rain extension which allows you to call it using something like click or on click? This allows the calling code to instead of having to call set my click listener. 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
typing a few less characters, but really it's on auto complete it anyway. But even worse what precedent will we be setting here by by adding us 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 Katy TX Library. If you're not familiar with the term, we call this code golf where you have that desire to create the shortest code possible. This is something we do not want to do. We're not here to just make the coach shorter.
Okay, there's another one that gets suggested every now and then and then I've seen people using. What's the Android because of the different ATI levels? We have to support you very frequently see these if checks around that the SEK. Scooby tempting 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 as an inline function means we don't have the overhead the lamb does the last parameters we get the nice kotlin call sites in socks and it turns out if statement from this into this
Now this by itself has is not too terrible. We're really not leveraging any of the language features again similar to the last one. Is it 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 a well for one thing is that you can at least static in Port SDK in and then there a little bit closer? That's one reason why this is left Justified. But one thing is that an if statement is a very primitive constructs of a programming language
and because an if statement is not just an if statement those 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 the case you either have to change back to using an if statement. Where you have to modify the function where maybe it takes to land us. Now one for the case where your above 19 one for the case where you're not Because we're not taking to Amazon this function. We've lost the special trailing land of syntax where we we now
have to pass them is arguments inside the parentheses, whereas before we didn't. So immediately this extension starts falling apart. If you introduce another conditional Branch, maybe we need to vary the behavior across apis in three different ways while there's really no way that we can make the extension to this. Dancing 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 lamp. We only want to run on, you know, 19 + y lot of times some
of the statements again if UK into will be less than or equal to in. So now we need a second extension in order to sport that uses. 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 for 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. You want the extensions to you allow you to express everything you would need to express if it didn't exist.
Okay. So those are all the extensions we just talking about this 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 touch on one. So for the fragment KTX, we have an extension which encapsulates transactions we move the begin transaction and the commit function calls into an extension. We use the fact
that we 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 use fragments, you'll know that it is not the only commitment function. There's actually more than one time so we can model this by doing something like allow it and you to supply a pool in as to whether or not you want to allow State lost or disallowed State lost when you're committing. Is really easy to accommodate. Sort of
goes against something I said earlier we're and we can update our call sites to go to use this it sort of goes against something I said earlier though where I talked about minimizing the impact of the implementation of these extensions. This is an inline function and then we've got put a conditional inside that inline function that conditional as being in lines into all the call sites until all the call sites now have to have that conditional inside of them. So is this actually a bad thing? Well, if you look at the bike code that gets generated from the call
site when we specify allow State last true. You don't have to understand what's going on here function calls. The first one is begin transaction. The second one is that replaced which was inside the Lambda and the third one is just a call to commit allowing stay Los. There's no if they've been here, there's no conditional. That's because since this was an inline function, and since the argument is a Boolean the compiler actually knows at compile time. What value your supply
And so since it knows I compile time and can actually do dead code elimination and eliminate the branches that can never possibly be executed. And so you actually get invite code what's equivalent to what you otherwise, whatever it There's actually more commit functions. There's one which allow you to commit now. It's a minute so we can also support that by adding an additional fully in the same thing happens here, even though they're now nested elimination will make it so that there is only one function call in the resulting by code.
Okay. As part of the this effort of all these releases that I owe one of the things that we've done is start creating a Collins Pacific View of the libraries that we publish and the Android framework itself. Love you. See you in a blue box there when you visit the reference reference stocks actually show you that I don't ask you if you want to do a Colin specific version of the platform or Android X Library. And also if you scroll down and that left navigation pane at the very bottom we have links to them as well and what these are
our island 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. That's no longer completely separate. One thing that's missing right now is that we we don't actually tell you the maven coordinates of the the artifact that these come from that's coming soon and also the extensions in Cork ATX which extends the 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 want to touch on you know, I'm here to talk about Angela KTX but calling extensions. There's nothing Android specific about it. What we're doing is building extensions to try and make these libraries more Colin friendly not something that any Library can do that's why I want to talk about the ways that we think about how to make libraries Mark Allen friendly that apply to both the Android libraries, but also apply to libraries that you
might be writing or you might to use it. The first way to make a library really calling friendly is just rewrite the whole thing in kotlin. I mean, obviously this isn't feasible for every library, but it's certainly an option for some if the library that's private to your app. It's in your repository or its internal to your company. And you're already using kotlin. This is a viable option. 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
may be 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 the main library remains written using Java language, language features as a sibling artifact. What's great about this as 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 ATI
where you get the Colin specific features and what's really nice about this since you don't have to control the library that you're sending. If you're just consuming a library and you want to make part of it more common 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 has But is this the only are these the only two options? I want to take a look at something that I think will lead into a 1/3 hybrid option.
Am I go back to this simple Alias extension where we taking the static methods if I do the Java language and turned it into an extension method in the Collin language? We look at the implementation of this class on the Java side. I've included the first line because we can see that it it it immediately dereference has the argument that we passed in Tennessee pass on destroying 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 Kawaii platform type. It has unknown all ability, but buddy from the implementation. We know right away that this method simply cannot extend cannot accept null values. No way we would fix. This is by adding the nominal annotation. I thought it was a sanitation does is it informs the Colin compiler that there's a restriction that there's a special behavior that it needs to take into account where it needs to
enforce that no one passes a potentially nullable value or are null into this method. This is something this is an enabling a language feature in kotlin that simply doesn't exist in Java. Now you can use the tools that will allow this enforcement to work for Jabba, but it's not intrinsic to the language itself. So we can do something like that for knowing us if we can have the sanitation for no less 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 the static method where the first argument is really the the receiver and can I say that this is actually going to be an extension function when I'm toddler. What this allows us to do potentially is eliminate the need to have this explicitly defined extension at all. Right 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. So now we're left with
just that the column compiler sees an annotation just like his father. No limitation infer something from it and allows you to call it in a way. That's more idiomatic for that wine. In the bike code we get what we otherwise would have written right? We still get the a 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 update padding not set padding know we can actually call this extension set pattern,
but the problem is that it will only work for a subset of argument. So in this case, we're we're just passing left and right values we could call that set padding it would work fine. But if we passed left, right and then top bottom we be supplying for arguments in the Colin compiler is going to see that the real set padding also except for arguments and it's going to prefer calling the real one in the real one doesn't have named parameters. So you're going to get a compilation are that's the reason we have to name this update padding. If we look at the
real set padding. simple method that takes for integers what if we could inform the kotlin compiler? That these parameters have names associated with them. Now it would be nice to infer this just from the parameter names directly not have to specify they were done and see but I'll argue that for one. It's very nice being explicit about these names in The annotation in Java bytecode there actually is a way for you to supply to to retain parameter names to the common compiler could in theory use those one problem is that then it
becomes an all-or-nothing thing. You have to you have to opt into this behavior. And then suddenly every parameter name across your library is set in stone. Where is with annotations? It's something that you could incrementally migrate. So the solves the this has the potential to solve the naming part. Where are now we can call the real method from Colin 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 can pass in what if we could specify an expression of call an
expression which allows the compiler to supply a default? When will Muslim to buy for you by you? This will change our original extension calling convention from calling are extensions. Actually just using the real method. And then in the by code we get this thing that we started with the thing that are explicit extension wood in mine too. 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 column specific way leveraging the colony. Until we're able to do so. This is something so Colin has this process which is called keep calling Evolution and enhancement process and just this morning we propose this these extensions or sorry these annotations as keep 110. This is something we're proposing to add to the Collin compiler so that it can understand is annotations. We have extension flexion and extension property you Witcher for static methods. default value which allows the flying default values for parameters And then Katie name, which
allows you to provide an alternate name for methods fields or parameters now, it's very important to note that. This is this is extremely early these names might change the semantics might change. This may never actually be accepted into the column compiler. We have been working with the Jeffersons theme for quite a while on this and it's actually already song. This is already prototype inside the Colin compiler. We really think this would be a way that we could enhance the Android framework for kotlin colors without
actually having to go and rewrite the Android framework or at least it's a TI and 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 column compiler. It doesn't totally solve every problem that our existing extensions are our solving we determined these annotations that we proposed and 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 with
the the pattern of java 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 you retain the single source of truth. You don't have to really know Collins. You don't have to add toddler compiler your build system. You don't the published tickling artifacts even fear of pure Java Library. You can add these annotations and just enhance your API so that Colin colors get that more idiomatic syntax.
All right for the summer Court Katy TX is now part of Android jetpack version with Android jetpack release 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 or slices and you model so I would not be surprised to see artifacts for those in the coming months. Please check out the Colin version of the reference document is extremely early where it is required changes
in Dhaka and and how we produce docs. And so it's something 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 or Android kcx because she acts and all the Katy TX libraries are are now part of Jetpack. The source of Truth has moved into the Android support repository. We're going to be migrating gift of the gab issues on the get up project over to this bug tracker in the coming
weeks. So it's important to know that we're still going to be accepting for requests to get a repo and thinking things back out to the was just that the issues will no longer be the source of Truth on GitHub. It will be on the Android bug tracker. I think the key was proposed by a credit report request about an hour ago, please go check that out the the document contains a lot more detail about examples. And like I said the invitations that were chosen we're the ones that we think have the most impact but at the bottom of the document you'll see that
there's if something like this gets accepted there's a potential for future enhancement. Even more. I'm dead of the link to that should be this. I made it link last night before I submitted it. So hopefully it's accurate. And that's it. Thank you.
Buy this talk
Access to all the recordings of the event
Buy this video
With ConferenceCast.tv, you get access to our library of the world's best conference talks.