Alright so we're going to talk about
singleton classes, and specifically the
AppDatabase class. So our AppDatabase
class should work and we'll be able to
test it soon. Before that though there's
a change I want to make - I want to make
it a singleton. Now a singleton class is
one that only allows a single instance
to be created. We've used a few singletons
so far - the most recent is the Content
Resolver. There's only a single Content
Resolver instance, although it's hard to
check that from the source code because
Content Resolver's an abstract class,
and the implementation's provided by
Android. Now we also created a singleton -
our Tasks Contract object. In Kotlin,
object is automatically a thread
safe singleton. Unfortunately though, we
can't make our AppDatabase class an
object, and that's because we can't pass
arguments to an object constructor. The
reason for that is because objects don't
actually have a constructor. We can use
an object in our class to return a
single instance of it. There's two stages
to making a class into a singleton. The
first step's easy - we mark the constructor
as private so that nothing else can
instantiate our class. So let's go ahead
and do that now. So we've got - up here
in our definition - internal class App
Database constructor. We're going to put
the private key word before constructor,
so we're now marking that as private. So
now the only thing that can create an
instance of AppDatabase is AppData
base itself, either via its own functions,
or functions in an inner class or
companion object. Now the reason I've
done that is because we only want a
single instance of this class to exist.
As well as creating our database, this
class also provides the connection to
the database.
Sqlite can support multiple users
but it locks the entire database when
writing to it. Our app is a single user,
but if we allowed many instances of this
AppDatabase class to be created, then
each one would, effectively, be a
different sqlite user, with its own
connection to the
database. Now that's not the end of the
world, and the app would probably
function just fine most of the time, as
long as several instances didn't all try
to execute the code to create the
database tables simultaneously, of course.
Now obviously, if you allow instances
of a class to be created at will, then
there's no way to guarantee that there's
only one instance. So making the
constructor private is the first step in
creating a singleton class. Now equally,
obviously, if there's no way to create
any instances of a class, then it's not going
to be much use to anyone unless all its
fields and methods are available without
creating an instance, as we saw with our
columns object in the Tasks Contract. So
how do we allow that single instance to
be created. Well you'll find many
examples of how to create Singleton's on
the internet. They're mainly in Java, and
unfortunately many of them are flawed.
Android is a multi-threaded framework
and class instances can be created on
more than one thread. Any attempt to
implement a singleton, has to cater for
the same objects being created at the
same time, from different threads. In Java,
one solution is to use a static inner
class which has a method returning an
instance of the singleton class. Now the
obvious solution of checking if the
instance is null, than creating a new
instance only if it is, doesn't work if
another thread calls the method before
the first call has created the object. So
that method has to cope with being
called from multiple threads at the same
time, and a common solution is the double
checked locking algorithm. So that
basically does a null check twice, and
locks the instance variable after the
first check. Now there's a bit more to it
than that, and as I've mentioned,
multi-threaded programming is extremely
difficult to get right. So let's have a
look at how we might implement that in a
companion object, and then I'll show you
our reusable solution. So our companion
object will have a getInstance function,
to return an instance of AppDatabase. So
I'm going to go ahead and add this at
the end of the class.
Alright so I'm going to come down here,
right at the bottom of the class
before the last right curly brace. We're
going to type companion object and press
ENTER there, and you can see that Android
studio fills out the rest for us. Then
I'm going to type private var instance
colon AppDatabase question mark equals null.
Then also what I'm going to do, is add
the volatile annotation to that
definition there, and then I'm going to create
a function called getInstance, fun get
Instance, and parentheses. We're going to
pass a context, so context : Context, then
outside of the parentheses : App
Database is equal to. Then on the next line
we're going to type instance question mark :
synchronized, in a parentheses, this. Then
left and right curly braces, and then
we're going to type within that curly
brace, within those curly braces, instance
question mark : AppDatabase. Then in
parentheses it's going to be context, and
then outside of the right parentheses,
dot also, and then we're going to
introduce left and right curly braces
again and type instance equals it.
Alright, so what have we done here? Well
we've used two Elvis operators which are
our to two null checks. On the first line, we
return instance if it isn't null,
otherwise the synchronized block of code
is executed. So that locks the variable
to prevent access from another thread.
Inside that block we once again return
instance, if it now isn't null, so if
another thread has managed to create it,
in other words. But if it is null, we create
a new AppDatabase instance and then
return that. Now the also function calls
the specified block, a simple assignment
here, and returns it. We assign the new
instance, represented by it, to our
instance variable. Now whenever this
function's called again,
instance won't be null and will be
immediately returned. Now don't worry too
much about exactly how this code's
working. As I've said a few times,
multi-threaded programming is very
difficult. Fortunately though, Android
gives us things like async tasks and
loaders to take care of a lot of
the complexity. Most of the time it's not
something we need to worry about. I mean
do you really want to worry about
synchronizing threads and using the
volatile annotation? You can click to
get a description of why that volatile
annotation's there. It makes changes to
instance immediately available to other
threads, and is one of the many things that
people get wrong. OK, so that code as
we've written it will work, but frankly
it's horrible. Having to modify it to
create different singleton classes could
be a good way to introduce bugs. What
would be really useful is some class
that we could use to do this all for us.
Now the good news here is that such a
class exists. There's a link to the
article about it in the resources
section for this video, and actually
you'll see that in a moment.
I'm not going to open the link in my
browser though, and that's because the
article isn't licensed for our
commercial use so I can't really go
through it all in this video. Now the
article may not be available for
commercial use, but we've had
confirmation from Christophe Beyls, the
author, that the code is. Now if you
want to use his singleton class in your
own code, that's fine. So what I'm going
to do now is create a new Kotlin file
class. I'll go ahead and do that, and I'm
going to call this one SingletonHolder,
and what I'm then going to do is paste
in the code. And what I'm going to do is
make sure that I delete these default
comments, because I'm certainly not
trying to claim any credit for this
code. So I'm going to paste that in there now, and
this is the code there from Christophe
Beyls, as I said. So as you can see
though, I have added a KDoc to show who
wrote the code, and also the URL for the
article. Have a read through the article,
especially if you want to learn a bit
more about how all this works. So the
only change that I've made to Christophe's
code is to add the usual logging, our
usual logging. There's only, and that's
only there really to make it easier to
understand what happens when we run the
app, and you'll probably want to delete
the constant logging line if you use
this class in your own apps. So this
class is basically a generic
version of the code I've just added to
our AppDatabase class in the companion
object. Alright. So what I'm going to do
is go back to that code and comment out
my companion object, and what we're going
to do is use the singleton class
instead. So what I'm going to do is, above
that commented out code, I'm going to
type companion object. Then what I'm
going to do is delete the left and right
clearly braces - we don't need them - and
instead I'm going to put colon. I'm going to type
singleton holder, then in a less than and
greater than sign there I'm going to type
AppDatabase comma context, and then I'm
going to type parentheses two colons and
AppDatabase. Now that's a lot easier. You
can use it in any class - it has a
single argument constructor just by
changing the two occurrences of the
class name. Now passing just one argument
to the constructor is normally all
you'll need. You'll often need to pass just
a single argument, a context for example,
to your singletons. Now I said
that's normally all you'll need, but I don't
want to imply that creating
singletons is normal. They're not
something you need to create very often,
but when you do need to create them, you
now have a way to do it right. Whenever
another class needs an instance of this
AppDatabase class, it calls the get
Instance method. The first time that's
called,
instance will be null and a new instance
of the AppDatabase class will be
created. And every time it's called after
that, the caller will get a reference to
the single instance of the class that's
stored in the instance field. Alright.
So in the next video we're going to add
some code to the onUpgrade function, and
then run a very basic test of the App
Database class. See you in the next video.