In the previous videos we created the
database that our app will use, and wrote
the code to create the Tasks table, and
we also added a few test records. Now the
app will use a Content Provider to
access the database, and we've seen how
to use a Content Provider earlier in the
course. So it's now time to see how to
write a Content Provider, so let's get
started with that.
Now once you've seen the code working,
it's definitely well worth your time
reviewing the Google documentation
about Content Providers, and let's just go to
that link in a browser. Alright so the
first section, in here, really helps you
decide if you need a Content Provider,
and we've got a few reasons there on
whether you need one or not. Now in our
case, we're going to be using a Cursor
Loader to retrieve the data from the
database, without blocking our UI thread.
Now as we do need one of the listed
features here, we're going to use a
Content Provider for this app. So Google
are now suggesting that developers
should use the new Room Persistence
Library, rather than Content Providers.
Now you might think that's a strange
thing for me to say when we're about to
see how to implement a Content Provider,
but there's a good reason why we're
sticking with it. The Room Persistence
Library is certainly exciting, but at the
moment it's not completely suitable when
you have linked tables. There's no
support for views, for example. Any
attempt to use SQL views with the
room Persistence Library will be an ugly
hack. Also, because the library's still
evolving, there's no guarantee that the
hacks would continue to work. There are
ways to work around that -
avoid using views basically - but for that
reason we're recommending at least at
this stage, you stick with Content
Providers when using linked tables. We
are watching developments carefully and
we'll keep an eye on things, but for now
though, we're going to see how to
implement a Content Provider. So let's
start by creating a new class, so
back to Android Studio. So for now
though, I'm going to create
a new class - Kotlin File/Class - and we'll
call this one AppProvider, and we'll
specify it as a class, not that it really
matters. And we're going to put a comment
here, relating to what the class is all
about, and we'll say it's the "Provider for
the TaskTimer app". and "This is the
only class that knows about" and in
square brackets I'm going to add an "[App
database]". And you can see now that that's
now a click-able link, now that
I've actually added that. And this is why
it's useful with the documentation because
now that it's click-able, I can click
through that and get straight back to
the class, noting that this link is now
active
in our AppDatabase class - I can click
back to there. So Android Studio does
take care of a lot of that documentation
when programming in Java, but it doesn't
do such a good job with Kotlin at the
moment. Alright so I expect that to
improve though over time. So it's well
worth including KDoc comments in your
classes. Alright so in relation
to this task, to this class,
we need to extend Content Provider so
let's do that. So we've got the usual
error there which would be relating to
functions not being implemented, so let's go
ahead and implement the various
functions. I'm going to select them,
press enter. Alright so as you can see
there's quite a few there that have been
implemented. Now it's going to be
interesting to log some information
when the functions in this class get
called. So I'm going to go ahead and create
the normal, the usual log
tag that we create for a class; private
const val TAG equals AppProvider. Next
I'm going to add a few constants and a
URI for the Content Authority. I'll
add them first and then we'll look at
what they're all for; private val CONTENT_
AUTHORITY equals, and that's
going to be learnprogramming
dot academy.tasktimer
dot provider. Then we want a private const
val TASKS equals 100, private const
val TASKS_ID equals 101,
private, oops,
fix that previous line, private const
val TIMINGS is equal to 200, private
const val TIMINGS_ID equals
201, and private const val TASK_DURATIONS
is equal to 400, and private const val
TASK_DURATIONS
_ID equals 401. Then last we want a
val CONTENT_AUTHORITY
_URI colon space Uri is
equal to Uri.parse, in parentheses
then it's going to be double quotes
content colon forward slash forward
slash $, and we'll select our
CONTENT_
AUTHORITY const. Alright, so a good
place to understand what these constants
are is the Google guide that we've
already opened up. So let's go back to
the browser, and if we come and scroll
down a little bit here, we've got this
section on Designing content Uris -
this one here - and that explains about
these Uris. So the authority is the
name of the provider and must be unique,
and that's why we use the package name with
dot provider appended to it. Package names
should also be unique when you come to
deploy your apps, so they form a good
basis for the authority as well. Now when
other classes access the Content
Provider - and that, by the way, includes
other apps as well as classes in our app
that use a URI - to make life easier,
the CONTENT_AUTHORITY
_URI has been set to public, the
default in Kotlin. Go back to our code and
have a look. You can see that on line 28.
Now that lets it be used outside of our
app because I've marked it
as public. Now the AndroidContacts
Contract class is also exposed as a
URI, and we used it when we called the
ContentResolvers query method in our
previous ContentResolver app. Instead of
having to find the correct URI, we just
used contactsContract dot
CONTENT__URI. So what is
a URI, and what's this Uri.parse all
about, again on line 28? Well a URI is a
Uniform Resource Identifier, and I'm sure
you'll be familiar with one particular
type of URI - the URLs or Uniform
Resource Locators - that we use to access
resources on the World Wide Web. So let's
go to the browser again, open a new
tab, and we'll do a quick search for URI.
It returns a number of useful links here,
but one that we want to have a look at -
we'll scroll down till we see it - this
one here RFC 3986 Uniform Resource
Identifier. So I'm going to click on that.
So this is the link to the original
spec in our RFC 3986 produced by Tim
Berners-Lee, who's credited with being
the inventor of the World Wide Web.
RFC, or Request For Comments documents
tend to be very wordy, but there's
generally no need to read the entire
document. If you wanted to write your own
URI parser, then you'd have to read and
understand everything in this document.
But to learn just a little bit about the
format of URIs, what we can do is to
scroll down a little bit and skip
straight to section 3 up here, by
clicking on the link in the table
of comments, of our contents rather.
And this section here under Syntax
Components has a very useful diagram of
the various parts that make up a URI.
So a URI starts with a scheme,
and you'll be used to schemes like http
or https that are used in URLs to access
content on the web.
The URL is a specific type of URI and
conforms to this specification. Other
schemes you may have seen are FTP, for
uploading and downloading files, and File,
used to display a local file in your
browser. Now section 3.1, if we just bring
that up on the screen there, that
describes the syntax for a scheme. So
basically a scheme must start with a
letter, followed by any combination of
letters, numbers, the plus symbol,
full-stop or hyphen. In practice, they tend to
mainly contain just letters. The scheme
that Android Content Providers use is
Content. So scrolling back up to
Syntax Components again, the next part of
the URI is the higher part - this part
here - which starts with two slashes
followed by the authority and a path. Now
when you enter https://
www.google.com into your
browser's address bar, the scheme is https.
Your browser parses that out of the URL
and knows that it must open a secure
connection. It then passes out the
authority, which in that example is www
dot google.com. For URLs, which again are a specialized
form of your URIs, the authority maps
to an IP address that identifies the
server to connect to. Now the page we're
currently look at, we're currently
looking at, has the authority tools dot
IETF.org which currently maps to an IP
address. And we can check that by starting a
command prompt or terminal session, which
I'm going to do now, and then typing in
a command which should work on all
operating systems.
So I can type ping tools dot ietf dot
org.
Alright I'm just going to stop that now.
And note that this command doesn't
work for all URL authorities. For
security reasons, many servers have been
configured so that they don't respond to
ping requests. The authority can also
include a network port as well as a
user-name - useful for sending emails for
example. So for the http and https
schemes, the authority specifies the
location of a computer service on the
network. Now for our content scheme the
authority specifies a Content Provider.
Alright so I'm just going to close that down.
Next comes the path separated
from the authority by a slash. So
the path itself can contain many
segments, each separated by a slash. So
the path for this particular web
document - and you can see that in the URL
up here - is html/rfc3986. For the
URIs that we use in our Content
Provider, the path contains the name of
the table, and may also contain the ID of
a row in that table. So that's really all
we need to know in order to understand
the URIs in our Content Provider,
but as you can see there can also be a
query and a fragment - these last two
sections here. Now we've used the query
part of a URI, when we specified the TAGS
and other parameters in the URL for our
Flickr browser app. When used in a URL, a
fragment's interpreted as meaning a
particular location within a html
document. So this page - looking again
at the URL - has a section named section -
3. Now we added that to the URL to get
directly to this section 3 of the
document. I want to say we added it - we
clicked the link. Now one thing that may
confuse you later,
is the use of a hash to separate the
fragment from the path. When we come to
parse our your URIs shortly, you'll see
the hash character used as a wild card to
represent any number. Now this is not the
same meaning. Our URIs won't contain
fragments, so bear that in mind
and treat the hash character in our code just as a
wild card for numbers, and not as a
fragment separator. To add to the
confusion, we'll be using fragments in our
app and they've got nothing at all to do
with URIs. I'll talk about them when
we come to use them, but once again, bear
in mind that the same word is being used
for a completely different thing, and
Android fragments have nothing to do
with URIs. Alright so there's a lot
more in this document, and it's certainly
not going to win any prizes for
literature, but this extensible
definition of URIs has allowed the
World Wide Web to continue working and
evolving for nearly 30 years.
So if it seems overly complicated, bear
in mind that the spec itself was
intended to be future proof.
There was no cloud back in 1994, but this
same system is used by Amazon Web
Services, for example, by specifying WSS
for the scheme. So that's really all we
need to know or understand about
URIs, because Android provides a Uri
Matcher class that we can use to make
matching our URIs easier. So let's
go back to Android Studio and then we're
going to create a variable to hold a
UriMatcher. So I'm going to add that
inside the class, and then we're going to
create that variable; private val
uriMatcher
by lazy. I'm just going to press ENTER
there to accept the rest of that code,
and then in left and right
curly braces. I'm going to type buildUri
Matcher, which will be a function
we're going to call, and in fact we'll
implement that in the next line. So
private fun buildUriMatcher
parentheses colon and it's UriMatcher,
and open left and right curly braces
there. And we're going to start with the
log, so Log.d parentheses TAG comma
buildUriMatcher starts.
And we're going to do val matcher is equal
to UriMatcher, and in parentheses
UriMatcher.NO_MATCH.
Then we're going to return matcher. So
we've set up now, our UriMatcher. This was
one way to do it, by creating a function
that returns a UriMatcher object. Now I
think that was a good use for a lazy
delegate to ensure that the function's
called, the first time we try to use our
UriMatcher, and that's why we've used
by lazy for our variable. Now we'll
fill in the details of that function
shortly when we've discussed what it's
going to do. I want to switch back, though,
to the Google documentation again in the
browser, and just below this note about
Designing an authority there's this thing
about Designing a path structure. Now we
can create URIs to provide access
to individual tables by pending the name,
or the table name, to the authority. Now,
for example, in the document you can see
here they've got com.example.app
name.provider, then a forward slash
table1. For the tasks in our code, the
full URI would be content colon
//learn
programming.academy.tasktimer dot
provider/Tasks. And for the
other tables, once we've created them, we
just change tasks for the name of the
table we want to use. The URI for
the timings table, for example, would be
learnprogramming.academy dot
tasktimer.provider/
Timings. So our Content Provider will be
getting different URIs for the
different tables and views, which means
we need some way to decide which table
is contained in the URI.
Now we could parse out the information
ourselves. It's basically a case of
extracting the name that appears after
the slash in the URI, but Google have
created this UriMatcher class to do
that for us. So the document here
describes what we have to do,
specifically in this Handling content
URI ID section. Now note that there can
be an ID at the end of the URI, which
makes parsing slightly more difficult.
So this Handling content URI ID
section explains why we need an
underscore ID column in our tables. The
provider uses that when it needs to
provide a single record, or row, matching
an ID, and the Cursor Adapter needs a
column called underscore ID to work. When
the user taps an item in a ListView or
RecyclerView,
it's the ID that's used to retrieve the
corresponding row from the databases,
using the underscore ID column. And
scrolling down here, we can see that
under Content URI patterns, that
there's a description which also
includes a link to the UriMatcher
class up here. And we'll have a look at
that in more detail in the next video.