What’s New in Room (Android Dev Summit ’19)


[MUSIC PLAYING] FLORINA MUNTENESCU:
Hello, everyone. I’m Florina Muntenescu, and
I’m a developer advocate. DANIEL SANTIAGO RIVERA:
I’m Daniel Santiago Rivera, and I work on the
Android Toolkit. FLORINA MUNTENESCU:
Room was created to simplify working with
databases in Android by adding compile-time checked
queries and an easier way to implement migrations
and an easier way to test. But since 1.0, Room added
more and more features. In Room 1.1, we extended
the RxJava support and we’ve added the raw queries. Room 2.0 just meant a
migration to AndroidX. But then with Room 2.1,
we’ve added several advanced features, like full-text
search and views. But then you told us that
there is room for improvement. So then, in Room 2.2,
we’ve been listening to some of the features
that you’ve asked and we’ve implemented that. So today, we’re going
to talk about what we’ve done in Room 2.0. DANIEL SANTIAGO RIVERA: Thanks. So since the beginning,
Room has always given you the capabilities
to define a dao and a query and then get some data back. And you’ve been able to
do this synchronously in a blocking way. But Room also shaped
with like data, and there was a way to get
observable data set results. So if something
changed underneath it, you would get a
new set of results. But it didn’t have anything
for, like, one-shot operations. That was mostly up to you. As development progressed,
we added support for Rx, and this was a complete story. You have types for
both observable queries and one-shot operations. Room also supports Guava and
this was a little backwards from Lifecycle. No observability but there
was one-shot operations. In Room 2.1, we started
going the Coroutines route and we added a suspend function. And these fall
into that category of one-shot operations. But in Room 2.2, we
added observable flow, and this completes
that Coroutine story so you can have a full
Coroutine application. But what is Flow? Let’s look at a rundown
on what Flow is. Flow is basically like a
relatively new Kotlin Coroutine primitive. The cool thing about it
is that it’s [INAUDIBLE].. So you emit some
data and there has to be someone on
the receiving side for more data to be admitted. The API itself is split into
two kinds of operations– there’s those that form
the data from varying intermediate operators,
like map and filter, and there’s those
that consume it. But let’s see some
code and some examples. So imagine we’re making the
best puppy-finding application and we need to provide an
API for finding some dogs and pet them. This has to be asynchronous
because sometimes finding dogs is tricky
and might take some time. We also have a parameter
here that limits the amount of dogs we get. If we were to build
this, we would start with a Flow Builder. And the Flow Builder
takes a lambda. In the body of this
lambda, you write the logic to be able to provide that. In this case, to get
our dogs and emit them. The receiver type
is a Flow collector. It only has one function, emit. So the interesting
part here is that emit is a suspend function. And it is because that means
somebody on the receiving end must be collecting the data
before this function completes and your body continues. If we were to call
the function, we use one of those terminal
operators like collect. If we execute this, we’ll be
getting a bunch of puppies and petting them,
which is amazing. On the other end, if we
use a limit of three, we would only get three dogs. And three dogs is still
pretty cool, in my opinion. In terms of room, in your
dao, you can now basically have a query function
that returns a Flow, and it’ll behave like
expected in the sense that you will get a
list of your result. But then it’s observable. And if we initialize it,
imagine we have a Room database. On the other end, we’re
trying to display our puppies. When we get it, we
get a list of puppies. But then if something
changes on the database, like we download
dogs from the cloud, you’re going to need emission. And this emission contains
both the old result set and the new one. The cool thing about Flow– and supporting Flow
in Coroutines– is that you can
start combining it with the rest of the
effort we’ve been doing with regards to Coroutine. Especially, you can combine it
with the Lifecycle extensions that will basically allow
you to launch a Coroutine. When the fragment
is started, you can start collecting those
dogs and start showing them, which is pretty neat. FLORINA MUNTENESCU:
So you’ve told us that you want to be able to
repopulate your application. So whenever your users
install your app, you want to be able to
already display the best dogs. So in order to do
that, you would use a prepackaged database. This database would
either be part of your APK or would come from a backend. So if you would have to
implement this by yourself, you would have to
do a lot of things, from opening the database,
validating the schema, locking the database
file, and a lot of things, taking away from
what you actually need to do– the features
that you need to build. So in Room 2.2, we’ve
added two new methods on the database
filter, createFromFile and createFromAsset. So createFromFile, we will
get as a parameter a file. This will be the downloaded
file that you have. So one important thing
here, you should make sure that you set View
Permissions on your file so Room is able to read that
file and copy that data. And then, if you are
pre-packaging your database in your APK, make sure you
put it in the Assets folder. In terms of working
with migrations, things are not very different. So in general, Room
looks at two things. It looks at the database
version installed on your device and at the version that
you declare in code. So in your @Database annotation. But now, with
pre-packaged databases, you have one more player. You have the
pre-packaged database. So when you’re talking
about migrations, for example, if the
pre-packaged database is version 1 and the one that
you put in code is version 3, Room will also have to handle
the migration from version 1 to version 3. So just keep this
in mind whenever you’re working with
pre-packaged databases. Next feature is relations. So this is not something new
for Room, but what we did in 2.2 is to increase the
support for relations. So today, let’s do a crash
course on what this means. So let’s say that an owner
can only have one dog. This is a sad world,
but let’s say that this is where we’re living. So this means that
we have a 1 to 1 relation between the
owner and the pet. And this can be translated
into database like this. We would have two tables, a
Pet table and an Owner table. And the Pet table
would also have a reference to the ownerId. For Room, this
means that you would need to create two entities,
one for pets and one for owner. And then, if you
actually really want to enforce this constraint
between the petOwnerId and the ownerId, you
could use a foreign key. OK. But how do we get a list of
all the owners with their pets? Well, if you want to do
this just using SQLite, you would need to
select first from Owner, and then you would
need to select from Pet based on the ownerId. So this would be put
inside a data class– a PetAndOwner class that
has the owner and the pet. With Room, you would annotate
the owner with embedded and you would add the
@Relation annotation for pet. So while this @Relation
annotation isn’t new, what is new in Room
2.2 is the fact that you are able to add this
annotation to an object that is not a collection. So for example, here, we’re
just adding it to the Pet and we’re telling
Room that there is a connection between the
petOwnerId and the ownerId. If you want to, when writing
your query in your dao, you would just select
from Owner and you would return the list of PetAndOwner. OK. So we said that an
owner has a dog. What if an owner can
have multiple dogs? So this will be a
1 to many relation, and this is mapped in the
same way in your database. So you still have
a pet and an owner. And then, when we want
to get the list of owners with all of their pets, the
queries that you need to write are actually the same. You still need to
select from Owner and then you would need
to select from Pet. But we don’t want
to do this by hand. We want to use Room. So we will write our data
class, OwnerWithPets, where we would still
have the owner embedded and the @Relation annotation
on the list of Pet. So here, in the
@Relation annotation, we’re saying that
there is a connection between the pet owner
of the Pet table and the ownerId column
of the Owner table. Our query is simple. It’s just SELECT
FROM Owner, returning a list of OwnerWithPets. OK. So we have an owner
having multiple pets. But what if a pet can
have multiple owners? Well, in this case, we have
a many to many relation. And unfortunately, this
can’t be expressed simply with two tables. But rather, what
we need to do is to create a junction–
a link table. A PetOwner table that
contains the IDs of the pet and of its owner. So the pet owner
would be an entity with a composite primary
key, the pet and the owner. But now, if we want to create
a list of all the owners with all of their pets,
things look like this. So first, we would need
to select from the owner, and then we would need
to write the query that creates an inner join between
the Pet and the Junction table. So this is quite a lot
of queries to write for this amount of data. So now, with Room, we can
write things differently. So we would still use the
same OwnerWithPets data that we’ve defined before with
our owner and our list of pets. Also annotate it with
@Embedded and @Relation. But what is different is this
associateBy tag in @Relation. So this is the one
that tells Room that, OK, you should connect
the pets and the owner based on this junction
table, PetsAndOwner. So now, in the dao,
we need to just write our query, SELECT
FROM Owner, and return the list of OwnerWithPets. So this means that,
now, in Room 2.2, with one annotation, @Relation,
we can support 1 to 1, 1 to many, and many
to many relations. OK, Danny, is there
room for more features? DANIEL SANTIAGO RIVERA: Yes. Yes, there is. There’s more features. Another thing we added in
Room 2.2 was default values. Not coupling default values,
schema default values. And you can find this in the
column in Flow annotation. There’s a new property
called defaultValue. And this actually takes
an SQLite expression. So you can use things
like [INAUDIBLE] which is a keyword on SQLite. Another thing we added in Room
2.2, or we made in Room 2.2, was a gradle incremental
annotation processor. And this is
basically a change we did so that you can
get some build benefits when you’re continuously
building your application. You have to opt in
into this feature. It’s Annotation Processor flag. We want to enable it by
default in the future, but we’re in this phase
where we’re trying it out, hashing out any issues. So please try it out. Let us know if it works or not. The last feature we had room for
was kind of like a long-lasting bug– or not a bug, but
like a long issue of dealing with a
sublist of columns. So imagine we had
this entity, Dog. It has a bunch of properties
and a smaller [INAUDIBLE] for it which has
a subset for it. If you try to query
the Dogs table but return a puppy, you will
get a warning from Room, telling you, hey, your query
has too many columns. So it’s returning too many
columns that you don’t need. And the reason this
is happening is because we were using
that star projection. With Room 2.2, we
added a feature that will actually
take star projection and rewrite it just to
the columns that you need. And you get that build
benefit, that performance benefit kind of for free. We call this
expanding projections. It’s an experimental feature. So try it out and let
us know how it goes. To enable it, you have to go
to your Annotation Processor options and also
up in via a flag. Expanding projections,
although, also tackles the long-lasting issue
of conflicting column names. And you could see
this a lot if you tried to do a join of
two tables that had two columns with the same name. For example, here, we
have Dogs and Owners and they both have
a column name ID. Expanding projections
solve this because you can define a [INAUDIBLE] and you
can use embedded with prefixes. And if you combine the prefix
along with the SQLite operation to rename the table,
then Room will figure out which column goes
to which entity and he will be able to
rewrite your projections so that there’s no
conflicting columns. So this makes it
easier for those queries with big joins that
have conflicting names. As you keep adding
more things, you can keep your star
projection, and Room will be able to figure out the rest. So to recap, we added
Flow support for Room. That completes the
Coroutine story. You can go ahead and use
coupling and Coroutines everywhere. You can actually also
replace Lite data with Flow. We feel it’s a nicer API,
especially because it has better error handling. Pre-packaged
database is a feature that’s been asked for a while. You can have that, too. We expanded relations,
the annotation, to have 1 to 1 and many to many. If you still have to do some
pretty complicated relations, there’s always the fallback
of writing your own queries. You get some sweet build
speeds with that incremental annotation processor. Schema default bodies
and expanded projections are also very useful. So try it out. Let us know if you have
any issues or problems. Come talk to us. We’ll be in the sandbox. Thank you. FLORINA MUNTENESCU: Thank you. [APPLAUSE] [MUSIC PLAYING]

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Copyright © 2019 Geted Tabs Online. All rights reserved.