So we left the last video with an error in main activity when we tried to call the putExtra function,
and you can see the error on line 50 on screen.
Now I said that putExtra can be used to add more complex objects to the intent, but there is a condition
that the object must meet for it all to work. Now that object has to be serializable.
Now there's a good description of what that means in Oracle's Java documentation, but rather than
going straight to the page I'm going to just search for it in Google.
I'll do a search for Java
serializable,
and there's a likely looking link right at the top there, but the actual second one,
Serializable objects the Java tutorials, is the one that promises to be the most informative.
And in actual fact I'll just go back there because the description really has enough information on the
screen there, "To serialize an object means to convert its state to a byte stream so that the byte stream can
be reverted back into a copy of the object".
So if an object is serializable it can be stored and retrieved. Now a byte stream can be saved to disk or
held in memory.
We don't know exactly what the putExtra method will do, but in this case it doesn't matter.
Let's long as it can serialize our object, it can store it in the intent for the new activity to retrieve.
Now go ahead and go back into that link again, because the full page may tell us how to make our photo
objects serializable. So that's handy.
The second sentence tells us that a Java object, up here, so a Java object is serializable if its
class or any of its superclass implements either the java.io.Serializable interface or its subinterface.
So basically all we have to do here is make our photo class implement that interface.
So let's go back to Android Studio and do that.
So we're going to go back to our photo object,
sorry our photo class I should would say, and what we need to do is make this serializable, and to do that we just come down here.
What I might do is make a bit more space, and on the end of that,
after the closing parentheses, I'm going to put colon Serializable,
and it's java dot io. Now in Java,
that's all we must do,
but there's one other thing that we should do, assuming that works of course.
Things are changing all the time and a week after I first typed that code,
Android Studio gives an error instead of importing java.io.Serializable.
Now if it was just a case of typing the import in manually, I'd probably have stuck with using Serializable,
but as well as causing that error, Jetbrains have added a really neat feature to the Kotlin plugin. Now
Jetbrains, in case you didn't know, are the people who produced the Kotlin language.
Now I've got a bit of a quandary now. Do I show you how to implement Serializable correctly, or do I show you
that neat feature instead.
So we've decided to do both.
Now this isn't something that we'd normally do, but there's a lot of comments on the Internet about
Serializable being slow on Android.
Now that can be true.
There's another interface that can perform a lot better than Serializable. That other interface
is called parcelable, and we'll come to that next.
Now the reason I'm going to show you both functions is because Serializable can also be faster than parcelable.
It depends on the structure of the object being serialized, and it also depends on Serializable being
implemented correctly, which often isn't done. Now
Serializable is very easy to implement.
As I said, all that's absolutely necessary is to add the implementation to the class declaration.
So that's in Java, but in Kotlin that's no longer enough.
Now Jetbrains are working on building Kotlin support for serialization,
but that's not yet ready.
So if you want to use it, we'll be using the Java serialization interface.
Now to specify that, rather than the Kotlin interface, we just add the import manually.
Now you can see in the case here,
when I selected, or rather when I typed in serializable, I selected java dot io in the pop up, and you can
see up here that under the import, we've got java.io.Serializable.
So if you do get an error, that removes the error, and we can carry on creating our class. Now Java code can
run on many different platforms, using different versions and types of the Java virtual machine
or JVM. Google have written their own JVM, Dalvik, earlier versions of Android, and ART on the later versions.
Now if you rely on the default implementation of Serializable, then the data might not be recoverable
using a different virtual machine or version of Java.
Now strictly speaking that's not going to be a problem here,
and that's because our serialized photo objects will only be used within a single app while it's running.
But it's always a good idea to do things properly.
So you may for example, decide to provide a bookmark feature to allow users to store certain photo details
in a favorites file,
and if Google subsequently update the version of Java or the JVM, then your favorites may not de-serialize
correctly.
Now considering how easy it is to fix that issue, there's really no reason not to do that.
So all we really have to do is add a serial version UID to the class. Now that needs to be a static field
in Java,
so that means we have to use a companion object in Kotlin.
So I'm going to add that to the style of a class just after the definition, before the override to
toString.
So I'm going to type companion object, press enter there so it writes some of the code for us.
We're going to type private const val,
and it's going to be serial, spelling and case is very important here, serialVersionUID equals one L,
so serialVersion
UID. Now the version number's used by the serialization code, to check that the data it's retrieving
is the same version as the data that was stored.
Now if you don't create your own serial version UID, then Java generates one for you, and different
versions of Java may generate different UIDs.
And that's why you can get problems by not defining your own
UID. Now because that's so easy,
many programmers just implement Serializable like this and then complain that it's slow.
Now with a simple class like this one,
this basic implementation will work just fine for what we want to do with it, especially on modern devices.
But the problem with performance arises, because the JVM has been given no indication of how to serialize
our class, so it has to use something called reflection to work it all out.
Now reflection involves examining the class at runtime, to work out the fields that need to be stored in the
byte stream.
Now we can do a lot better than that by implementing three additional functions.
Now the details of these as are as described in the documentation for Java dot
io dot Serializable, and the quickest way to get to that is just to control click on serialization in our class
declaration.
In the case of a Mac I'm just going to do a control click.
Now I'm not going to go through this Java doc but if we scroll up into round about line 60, from memory, there's the
three functions that we should implement.
So I'm going to start with write object, which puts the value of our class properties onto the output stream
that's used for serialization.
So I'm going to close that down, and back in photo we're going to do that. So we're going to type in this
after the toString function that we've written.
So we're going to start with an annotation, at throws
parentheses IOException
colon colon class, then we're going to start with the definition which is going to be private fun write
Object,
that's that first one. Then it's going to be out colon Java dot
io dot Object
OutputStream. Then we'll start with a log,
so Log.d parentheses tag, and this time we're just going to type photo because we haven't actually created a tag constant here,
and do a writeObject called.
Then we're going to do out dot writeUTF parentheses title.
I'm just going to copy that,
the remaining fields. We need to do author, author Id, link, tags and image.
The next one is going to be author, author Id, then we want link, next we want tags,
and finally we want image. So that's the Kotlin equivalent of a Java signature that the function
must have. Now I've logged when it gets called,
and then we just write each of our properties to the stream.
Now the next one's a complementary function, to take data off the stream and set the values into our
class properties.
So let's go ahead and do that one.
We'll start with an annotation again.
So at Throws parentheses, it's going to be IOException colon colon class comma, then ClassNotFoundException
colon colon class, then the actual function definition will be private
fun readObject parentheses inStream
colon. Then it's going to be java dot.
io dot ObjectOutputStream,
and the code, we're going to do a Log.d Photo again
comma
readObject called, and the code's going to be title equals inStream
dot readUTF.
I think that should be object dot inputStream, let's try that.
I'll just stick the last one there.
OK author is equal to inStream dot readUTF,
then we've got author Id equals inStream dot readUTF, link equals inStream dot readUTF, two more, we've got tags equals in
Stream dot readUTF,
and the last one is image,
so image equals inStream dot
readUTF.
Now we've got an error on each line,
when we try to assign the value from the stream to our properties, and that's because all the properties
were declared as foul so we can't change them.
So to fix that, we just have to declare them as var in our primary constructor, or in the primary constructor.
So let's go back and do that,
so we're going to do that to all of them. So var, var, var, var, var, and the
last one image, var, and you can see that fixes the errors.
OK now the last function that we now have to implement is the read object,
no data.
So let's go and write that one now.
So it's going to be at Throws parentheses ObjectStreamException colon colon class, and the function
definition's private fun readObject
NoData parentheses, and it's going to be just a Log.d
Photo comma readObjectNoData called.
Now don't worry too much about this function.
It's intended for rare cases when you've created a subclass of the class that was originally serialized.
Now we're not doing anything in this function.
I've included it so you can see the Kotlin signature for all three functions.
So when we provide these three functions, the JVM no longer has to try to work out what goes where, when
serializing or de-serializing the data.
So this makes the process far more efficient.
Alright so at this point we should find that the error's now fixed back in main activity.
Let's go back and check that out, and we can see the intent dot putExtra has no longer got any errors.
Now in my main activity's on item long click, we're using the putExtra function to store a photo object in the
intent.
Now the key we use comes from the photo transfer constant defined in the base class, and we talked about
that before.
Now we get the photo details by using the get photo function of the adapter, and telling it the position of the
photo we want. And we have to start the activity which we do by calling start activity, and
you can see that happening on line 51.
Now what we haven't done yet is written the code to download and display the full size photo, but we
can run the app to make sure that what we've done works so far, and that there's no errors.
So let's go ahead and do that.
The list can be scrolled as it could be before. When I tap on an item, we can see the Toast message
should still appear, which it is
as you can see there, so we haven't broken anything so far.
So I'm going to try a long press now, and let's do that, and you can see that at this point, there's our
photo details activity launched.
Now it's just showing the placeholder at the moment,
but we have got it launching successfully.
Now we've also got the home button in the toolbar as well, so that bit's also working fine.
Now the button doesn't work though, if you try clicking it. Clicking it should take us back to main activity but
that's not working.
The back button down here though, does work,
but again you saw that the home button doesn't.
Now the reason that the home button's not working, is that we didn't use the hierarchical parent option
when we created the activity from the wizard. So we ticked it for search activity, but not for photo details
activity.
So let's have a look at what it did, when we ticked that option and chose main activity as the parent for
search activity.
So we're going to open up the manifest. I'm going to
close down the log cat for now. We can actually see the differences between the two, between the two activities,
the search activity and photo details activity. Search activity's got an extra attribute in its tag, and also
a meta data tag.
Now these tell the Android framework that search activity's parent is main activity, so it knows where
to go back to when the home button's tapped.
Now that's all that ticking the option in the wizard actually did,
but if you forget then, then it's very easy to do it manually afterwards.
So I'm going to go ahead and add the parent activity name attribute to the activity tag and copy the meta
data.
So I'm going to start over here.
I'm in the search activity. So I'm going to copy this line here,
this android parentActivityName equals MainActivity.
Copy that, I'm going to come down here and put that into photo details,
and I'm going to put it right at the end here,
after photo details, after the label in the same place as it is, currently in the search activity.
Now the other thing we need to do is copy this meta data, so let's go ahead and do that as well.
So I'm going to copy that, and put that below down here, after the NoActionBar line.
So if we run the app again now,
and we'll just test that the home button now works.
So I'm going to do a long tap and click on the home button, and now it goes back to main activity correctly, so that's working
well.
Alright so let's finish the video here.
We've still got a bit of work to do. In the next one,
we'll start talking about how to get the photo details activity to display the photo.
So I'll see you in the next video.