[MUSIC PLAYING]
YIGIT BOYAR: So today, we
are going to talk about Room.
So we asked people to write
offline-ready applications.
We want your application to work
without the network connection.
But if you don't have a proper
model inside your application,
it's pretty much impossible
to write a good offline
experience.
So for that reason,
you do need a database.
Luckily, on Android,
we have SQLite.
And SQLite is a really,
really awesome technology.
It's very fast,
and when you need
to optimize it for your use
case, it's very easy to do so.
It's a very powerful
query language.
You can express many different
things, and make it concise,
and easily grab the data.
It's the biggest advantage
compared to other object
stores are key-value stores.
And SQLite also
scales very well.
I mean, for an application, you
probably won't have much data.
Well, you can have
multiple gigabytes of data,
and SQLite will be just fine.
And there are companies
using it on the back end.
So for your scalable
to data size needs,
SQLite will be perfect.
Now, SQLite on Android
was not that cool.
You need to write out
a boilerplate code
to convert between your Java or
Kotlin objects and your SQLite.
There is no compile time safety.
So if you're building
an SQL query,
and you forget, like,
a one comma if case,
you're going to get
a runtime crash,
and it's very hard to
test all those cases.
You also cannot observe
what has changed.
Now, we want people to
write reactive applications
or reactive UIs, and if you
cannot observe your data model,
it's kind of hard.
You have to build it yourself.
So we built it for you.
So around two years
ago, we shipped Room.
We introduced a
compile time safety,
we introduced observability,
and we introduced a strong IDE
integration.
As you can notice with
Room, with Navigation,
this is a big theme for us.
We want to develop libraries
together with Android Studio
to provide a nice
user experience.
Then this year's I/O, we
introduced write-ahead logging,
which speeds up SQLite a lot.
And we also introduced
support for big paging
so that you can have very
large datasets and queries,
and you can easily load
them into a recycler view.
2.0 release is just our
conversion from Android Support
to AndroidX.
And we kept it the
same as 1.1 point
so that you can have
an easy migration.
And 2.1 is what we're
going to talk about today.
And this is actually
kind of the X of 2.0.
It's a really large release.
We have full-text search, VIEWs,
multi-instance invalidation,
AutoValue, and more RX stuff.
So let's talk about them.
DANIEL SANTIAGO: Sweet.
One of the pretty cool new
features we added in 2.1--
it's Full-Text Search, or FTS.
And FTS is basically a way
to index text documents
and make them searchable.
Let's take a look at an example.
Imagine we have a music app, and
we want a search functionality
to it.
You have a search box.
You want to type
something, and you
want to be able to find
songs with that music.
If we have Room, we express.
We store the song
data in a table.
That's an entity.
Conveniently, we have our
label as an embedded object.
And our song labels--
what's the song name, the
album name, and artist name--
this is kind of
like what we want
to search and make the index.
If we were to do this
without FTS right now,
we need to write a query.
And basically, you would have
to use the LIKE operator.
This is not very good.
It's very limited.
That percentage sign is
kind of like a wild card,
and this basically
causes a full-table scan.
YIGIT BOYAR: Yeah,
even if you index that
query column in the
database, SQLite
will not be able
to use that index,
because the index
only works if you're
adding a prefix search, which
is not what you want to do here.
DANIEL SANTIAGO: Yeah,
so don't do this.
Moreover, if you try to actually
search across album or artist,
you have to expand this query.
And this, as you can see,
doesn't easily scale.
FTS helps us with
this situation,
because it now creates a virtual
table and all the columns
are indexed.
And to use FTS, you just now
annotate your entity with @FTS.
Now, in your query,
instead of using LIKE,
you would use MATCH, which
is a different operator.
And as you can see
here, we use WHERE Song.
So we're using the same
column network statement,
and that basically
tells the MATCH operator
that you want to search
across all those labels.
So this helps us with searching
across artists and albums
if you have an omni box.
You might say, oh, then I can
use FTS on all of my tables
if I have an [INAUDIBLE],,
but not quite.
So using FTS consumes
more disk space.
And the reason is because
when you create an FTS table,
you actually create
a virtual table.
And that's backed by a few
tables where your content is.
And a lot of that's
indexing information.
This is known as shadow tables.
When you actually query
from your virtual table,
the information actually
comes from these tables.
There's also a few
drawbacks to FTS.
You cannot have foreign keys on
your entity or compose primary
keys.
But there's one
pretty new feature,
which is external content.
Going back to our
song entity, if we
wanted to instead
use our real table
and create a second table
for only our labels,
we just basically use
that FTS annotation,
but we tell it, hey,
my data is actually
going to be stored in this other
real table that I already have.
Conveniently, this new data
class and new virtual table
only has the labels.
So in our previous
example, everything
was indexed, even the URL,
which is not quite what
we want to index.
In this case, we
only have the labels.
What happens now is we have a
virtual table in front of it.
It's on FTS, and behind it,
you have the same shadow
tables for an indexing.
But the actual content we store
in the original Song table
that we have.
This is way better
in saving disk space,
and it's a little
bit more flexible.
To query this FTS table
with extended content,
you do have to query
from the virtual table,
and then you would
do a join, because we
want to get the songs.
And then similarly, you
will still use MATCH.
One thing, though, is
that, because these
are two different tables, when
you insert into the Song table,
things are not actually inserted
into the virtual table, FTS
table, which means your
index doesn't get updated,
so you have to do that yourself.
But we don't want
you doing this.
We want to make it easy.
So when you use Room,
Room will actually
create triggers for you to
keep these two things in sync.
That's pretty cool.
YIGIT BOYAR: Another important
feature we have added
is support for database views.
So let's go back our
song and album example.
We have songs and albums, and a
song might be multiple albums.
So we have a junction table
that associates the songs
with albums.
Now, this is all cool.
So let's say you want
to fetch a listing.
You want to have the album
name and all the songs in it
as a list.
OK, cool.
We just write it.
We have the listing [INAUDIBLE],,
and we write a query,
and fetch from that
junction table.
Unfortunately, you
cannot do this,
because that table doesn't
have the song's name
or the album's title.
You kind of need to write
a query like this, where
you fetch from that table and
join it with the Song and Album
table, and then you can
return your listing data.
Now, this is actually cool.
SQLite is powerful that
you can express this.
But if you find yourself
keep writing these things,
it's kind of like lots
of boilerplate, things
you need to keep in your mind.
Wouldn't it be cool
if you could just
have a table that has
the song and the album
together without you
duplicating the data
into that table and the
Songs and the Album tables?
And this is where database
views come into place.
So you basically write the query
that defines Album and Song
together as a query.
You annotate an entity
with the database view.
So you're saying that it's
like view to this database.
And in that [INAUDIBLE],,
this is the same Room rule,
so you can have any [INAUDIBLE]
with the embedded fields
or what not.
Once you declare it and add
it to your Room database--
so we have that declaration.
If you tried to rewrite
the previous query,
then you just get rid of
all the joins and instead
select from that table.
Well, we are
selecting from a view.
That table doesn't exist.
But for all intents and purposes
of querying, that's a table.
Now, it's much more simpler.
Because I said
it's like a table,
you can also return
that [INAUDIBLE]..
Or you can even
return the live data.
Because we know how that
view was constructed,
we know when the
value might change,
so you could get
the live data of it.
You can run queries.
You can pretty much do
everything you can do with
a table, except you cannot
do inserts and updates,
because there is no baking data.
But you can have views
inside other views.
All that stuff works.
So this makes it much
nicer to write queries,
and it also allows you to
logically address your data.
Another important
feature we have added
is support for
multiple instances.
So let's say we are
writing the application.
So we have a playlist,
all the songs,
and we have a sync service
that goes to our back end,
pulls the name, updates
from my playlist,
and writes them
into the database.
When you are using Room,
if the sync service
updates the database, it
automatically updates the UI.
And this is a
super cool feature,
because you write these
two components absolutely
independent of each other.
They don't know
about each other.
They have the database
as a sync point.
This works perfectly, but then
your startup is successful,
you grow your team, your
application is bloated,
so you decide to model
that synchronization
into a background process.
Now, when it's running
in a different process,
it pulls the song, writes
it into the database,
and the UI has no idea.
It doesn't know the database
has changed, because it only
knows that database is changed
if the same Room instance is
the one making the updates.
So we can fake it, because
we don't get that information
from SQLite.
There's something [INAUDIBLE].
Now, with Room
2.1, you can enable
multi-instance invalidation when
you build the Room instance,
which will look for other
instances of Room that are
accessing the same database.
Once you do that, now, your
background process service
can update the database,
and all instances of Room
will update automatically.
Now, we don't do this.
This is off by default, because
we need to create the service.
There's some IPC involved.
It's not a big cost,
but it is a cost
that most people don't need.
So you need to enable
this flag to take
advantage of this feature.
DANIEL SANTIAGO: Another
feature we added,
which was actually
requested by the community,
was automatic support.
If you're using
Kotlin, you don't
have to worry about this,
because you have data classes.
But if you're still
in Java world,
then you might be using
auto value, because you
want Java immutable objects.
Well, Room, now, can
understand these auto value
annotated objects.
If you know a little
bit about auto value,
you basically have
an abstract class,
and you annotate
it with AutoValue.
But now, you can also annotate
that same abstract class
with @Entity, and Room
will be able to discover
that you want to make a
backing SQLite table for it.
In auto value, instead
of having fields,
you have abstract getters.
These can now be annotated
with Room annotations,
so their primary key, column
information, and things
like that.
The only caveat,
though, is that you
do have to also add auto
values CopyAnnotation,
and this is the annotation
that basically makes
these two tools work together.
YIGIT BOYAR: By the way, to
support this, so normally
these annotations were
only limited to fields,
and we needed to extend
it to let you put them
on those abstract methods.
But it only works if
you're using auto value.
If you're not using
auto value, we are not
going to let it compile.
DANIEL SANTIAGO: Similarly, if
you were writing a normal data
class, you would have a
constructor with the fields.
In auto value world, you still
need that factory method.
And Room will be able to
discover this to create
your auto value class.
And then using it, you would
use it at any other data class.
You would use the abstract
class that you declare.
Another highly
requested feature that
has been requested for
a while-- and we finally
got it-- is more Rx.
So now, you can actually
have asynch return
times Completable,
Single, and Maybe,
in methods annotated with
Insert, Update, and Delete.
So you know we listen.
And when you request
stuff, we'll listen.
[LAUGHS]
YIGIT BOYAR: Yeah, this is
actually only available in Rx.
That's interesting.
DANIEL SANTIAGO: It might
be available in another type
of asynch.
YIGIT BOYAR: Like
[INAUDIBLE],, Kotlins?
DANIEL SANTIAGO: Maybe.
YIGIT BOYAR: Yeah, please.
So Room 2.1 is a
really big release.
The full-text search,
the database views--
and when we decided which
features to work on,
we are basically relying
on your feedback.
I personally objected
that allowing complete
of less stuff in those Insert
queries for a long time,
and I gave up, because
people really wanted it.
And this is our
development philosophy.
We basically look at what
the community is doing,
how are they using it, what do
they want, and implement them.
So please, try to use to 2.1.
It's a very big release.
And we want to just ship it
as stable as soon as possible,
and we need your feedback.
We basically look at the number
of apps shipping with Room
and see how they are using it.
We'll look for
the incoming bugs.
I mean, we don't really
have bugs, but sometimes--
DANIEL SANTIAGO:
No bugs, no bugs.
YIGIT BOYAR: --look for incoming
user errors, try fix them.
And [INAUDIBLE] to ship.
So please work
with us, and we'll
try to wrap it up and ship it.
And also please let
us know what other
features you want in Room so
that we can implement them.
All right, thanks a lot
for coming to this talk.
I hope it was useful.
[APPLAUSE]
DANIEL SANTIAGO: Thank you.
[MUSIC PLAYING]
