A while ago I came across the Google I/O App in one of the latest Android Developers blog posts. I thought it would be interesting to have a look at some of the internals, to also gain some insights at how Android applications are developed at Google and what third party libraries are actually used there.
The source code for the Google I/O app is available on GitHub.
My journey through the source code began with the settings.gradle file. It contains the information about the projects Gradle modules. This app consists of two modules: one for the wearable version and the other one for the Android version.
This article will not talk about the implementation of the wearable version, I will create a separate blog post for that.
I have to say that the Android version was of particular interest for me, so I decided to go on with the Android modules build.gradle dependencies section that holds all the external dependencies that are needed by the implementation:
As you can see in the code snippet above, several external dependencies have been included.
Let’s start with the first dependency that gained my attention:
The Android dash-clock project comes with an alternative lock screen clock widget implementation that can be used to show additional status items. Showing additional information on the lock screen is done by implementing so-called DashClockExtension extension descendant classes, as described in the DashClockExtension documentation. Although this API looked pretty interesting, I couldn’t find any use for it in the Google I/O application and also removing it from the dependencies did work, so I guess it might have been planned to use it, but actually it was never implemented.
The Google I/O app’s main purpose is the give an overview of all the scheduled talks at Google I/O and also allow some interaction for the user to give feedback about visited sessions. Gson is used to parse JSON that comes from Google’s web services and contains the entire conference data.
One particular piece of code that shows some Gson usage is the ConferenceDataHandler. This handler basically is responsible for parsing most of the JSON data that holds information about the scheduled conference sessions, speakers, etc. Instead of parsing the JSON content directly to an object tree, it registers “handlers” for every JSON property in a map:
With the registered handlers set up, it parses the JSON response body property by property in processDataBody:
When we have a look at one of the handler classes, let’s say at SessionsHandler, we will see that it not only encapsulates the code for parsing the session JSON objects, but also code for building so-called “content provider operations”. The ContentProviderOperation class is a class from the Android SDK that is used to build content provider actions such as inserting, updating or deleting entities stored by a content provider. The handler classes provide methods to directly create content provider operations based on the current state of an entity. E.g. if a session is new, needs to be updated or deleted, its makeContentProviderOperations method from the handler class will create the appropriate operation. Let’s have a look now how actually parsing JSON is done for the SessionsHandler:
The code is quite slick. It uses an array of Session model classes as GSON target type and GSON will create the instances and populate the available properties from the JSON values:
What’s interesting about this class (and the other model classes) is the getImportHashCode method. This method is needed to find out about changes that might have been done on already processed entities and is actually a main method to be used by the data sync logic implemented by the SyncAdapter.
Next up in our list of dependencies is the Google APIs client library and its Android extension. Both libraries are used in conjunction with the Google Plus API from the next dependency
to fetch the latest announcements via the AnnouncementsFetcher class. Once the announcements are fetched from the Google+ profile, they are stored by the content provider ScheduleProvider:
Again, the ContentProviderOperation builder methods are used to create the appropriate operations and return them to the class client.
The SVG Android project adds support for showing scalable vector graphic files in an Android application. In the Google I/O application it is used to show the location of different floors in the Google I/O venue.
One place to have a look at SVG processing is the ConferenceDataHandler implementation, again, a handler class:
The code looks if the SVG graphic is available in the APK’s asset directory. If so, it copies the file to a custom directory. If not, it downloads the SVG and uses the svg-android library to validate if it is a valid SVG graphic.
The main place where the SVG graphics are later used is in the MapFragment implementation. It uses a TileOverlay and registers multiple TileProvider implementations of type SVGTileProvider class. The SVGTileProvider uses the previously shown SVGBuilder in order to draw the currently shown floor onto the map.
As can be seen in the code above, the method getTileImageData applies some scaling and translating, but in the end it draws the mSvgPicture onto a newly created Canvas and writes it to the resulting ByteArrayOutputStream. In order to enhance performance on creating the tile graphics, there is the CachedTileProvider implementation that uses a disk LRU cache to cache results on disk.
I found it very refreshing to see an application of the svg-android library in action. Its definetly an implementation option to carry in mind for future Android apps.
Glide is an image loading and caching library that comes with extensions to other commonly used libraries such as OkHttp and Volley. In the Google I/O application the Glide API is encapsulated in the ImageLoader class.
One interesting detail in this class is the VariableWidthImageLoader implementation:
The VariableWidthImageLoader is used by Glide in order to return a customized URL that should be used for a given width and height. The implementation above looks for an image indicator in the current URL (think of model as being an URL to an image) that might look like __w-200-400-800__. If this indicator is available it replaces it with w<desiredWith> to actually fetch an image with a width that is actually larger than the requested width.
We used a similar pattern in our applications for image URLs (though with a width request parameter), but I wasn’t aware of Glide providing such a nice API to inject this behaviour.
Basic HTTP Client
Of course, the Android [basic http client implementation] (https://code.google.com/p/basic-http-client/) must also not be missed. It is needed to execute the actual HTTP requests for example in the RemoteConferenceDataFetcher that fetches the JSON content from Google servers. In fact, it first fetches only a so-called manifest file and checks whether data has changed based on that manifest. A detailed explanation on the actual synchronisation of the conference data can be found at the Android developers blog.
This article had a look at some places in the Google I/O Android application and showed some third party libraries in use. The application has been open-sourced on GitHub and is available under the Apache license.