You like testing? I love testing, especially unit testing. ContentProviders are the underpinnings of many data layer implementations in Android apps and obviously, an important thing to test. I added some new code to a ContentProvider in the RadioPublic app and wanted to verify that the ContentProvider and model code worked. I spent an hour looking through the documentation and online. I also wanted to use the Robolectric test framework already setup in the app. After concluding my research, I found what I was looking for and its very straightforward.
First of all, if you haven’t already done so, in your module’s build.gradle file, depedencies section:
As I sit here and watch the Cleveland Cavaliers take on the Toronto Raptors in the NBA Semifinals on the WatchESPN app on my Nexus Player (running Android TV), I’ve realized that I’ve just hit the point of no return as a cord cutter. I originally got rid of cable television almost 2.5 years ago because I got fed up with the garbage user experience and the price. By price, I don’t mean the final price, but I mean the terrible fees for HD, modem rentals, set top box rentals, taxes, etc that cable and telephone companies nickel and dime you with. When I cancelled, I was paying AT&T $140 for UVerse TV & Internet (18Mbps) on one television. I considered $20-$30 of this bill to be BS. AT&T charged you $5 for high definition television, $7-8 for the set top box, $5-7 for the AT&T Gateway and then miscellaneous taxes.
So I quit television cold turkey and quickly signed up for Netflix and Hulu. I used a Sony Google TV as my set top box. Google TV was a terrible experience at the time, but I wasn’t being nickel & dimed. Fast forward to today. I pay for Netflix ($8), Hulu ($10), HBO Now ($16), and Sling TV ($20). I watch all of my TV on my Nexus Players (2) and over the air (via an OTA TV antenna) I pay Time Warner Cable $65 for 300Mbps/25Mbps internet service (I own my own all the hardware). I pay $119 every month for all of this stuff, but my experience is significantly better. Not only do I have incredibly fast internet, but I can watch any show from any of my subscribed services on any of my devices using apps that aren’t bad…in fact some of em, like the HBO Now app are excellent. A big bonus is the ability to cut one or more of these services without making a single phone call.
I’ve essentially reached a point of no return. I’m no longer considering subscribing to cable for NFL football because I can watch football either OTA or on ESPN. I’m a NY Jets fan and I will miss out of network NY Jets games, but a normal cable subscription doesn’t provide this anyway. So I don’t miss what I’ve never had. Luckily, I’ll be able to weirdly watch Thursday Night Football games on Twitter.
I say all that to say this, cord cutting is not about pricing. Cord cutting is about the user experience. It is my belief that television and other video content will move from the cable model to apps integrated onto existing platforms like Android TV, Apple’s tvOS, Android, iOS, and Roku because the experience is significantly better.
The next version of NC Traffic Cams (version 3.0, but with a different name) is nearly feature complete. I completely overachieved on this project, but purposefully. Since I stopped actively building PremoFM, I wanted to start on a project that would teach me a few advanced Android development tricks. I’m building the next version of NC Traffic Cams with the following in mind:
Model – View – Presenter, similar to Model – View -Controller, enforces the separation of Android specific logic and business logic in an effort to make the business logic more testable. I’m also using Loaders to keep Presenters around. This is great because it means I have to do no work to persist the apps state during a device rotation.
Lots of unit tests.
Lots of RxJava, in the latest released version of RxJava I wrote a ton of AsyncTasks. This time around 0 AsyncTasks. I have RxJava to thank.
OrmLite for the data layer, no more writing SQL selects and inserts by hand (I actually tried to integrate with Realm, however, I threw it out because of the thread management issues I encountered).
OkHttp / Retrofit / Gson for the API later, no more writing HttpUrlConnection or JSON parsing logic. I have a simple API setup for NC Traffic Cams and Retrofit made it ridiculously easy for me to get that data into NC Traffic Cams.
I wrote substantially less code this time around and the app is better, more stable, and more performant. Can’t wait to show you all what it looks like. It’ll be done soon™.
Here’s NC Traffic Cams v1 & v2 since it’s #ThrowbackThursday
I just uploaded the source to my podcast app, PremoFM, to GitHub. PremoFM was my attempt to create the podcast player I loved using. I was super successful at that. However, I built PremoFM with too many costs, mainly servers. It didn’t make a lot of sense to keep that going with no revenue so I ended it.
However, PremoFM was super important to me and crucial to my career as a now, full-time, Android developer. It is a modern Android app that embraces Material Design and some of the latest APIs available on Android. I want to share this with the world.
What are the top three aspects of PremoFM that make me the proud?
The podcast player service is baller. I built an Android service that leverages new MediaSession APIs introduced in Android 5.0 “Lollipop”. I also refactored that actual MediaPlayer so that I can seamlessly switch between MediaPlayer (plays media through the phone’s speaker or headphones) and the CastMediaPlayer (plays media through a Google Cast device). The LocalMediaPlayer was built on Google’s awesome, open source media player, ExoPlayer. I was one of the first apps to build an experience around ExoPlayer.
User interface and design. PremoFM was not the prettiest podcast app, but I thought it was the cleanest and most intuitive. It presented everything you wanted to look at up front and didn’t require the user to do too much digging.
Notifications and the Download service. PremoFM never polled the API server for new episodes periodically. A Google Cloud message was sent to the device when new episodes were ready. This allowed me to alert users of new episodes almost immediately. The Download service cached episodes and podcasts in the background in a battery efficient manner because it was built on JobScheduler (introduced in Android 5.0 Lollipop). JobScheduler allowed PremoFM background services to run in the most battery efficient manner, when other things were happening, like the Google Play Store updating apps.
What are the top three disappointments?
Architecture. I failed to separate lot of the business logic from Android-centric components like Activity, Fragment, and Service. This made it really hard to write tests. If I had to do it all over again, I would use a modern Android development architecture like MVP (Model, View, Presenter)…that would enforce some code and logic compartmentalization. It would allow me to easily write unit test for important business logic without needing to worry about mocking Android APIs and components.
Database stuff. I wrote all of the database logic from scratch. I built the entire app on ContentProvider. While it worked well for PremoFM, it was a headache to maintain. I often introduced bugs because I’d add a column and would forget some important aspect like making it nullable. I did not have a ton of unit tests so many of these problems made it to beta when they could have been prevented earlier. If I were to change something I would use a library like OrmLite to make this portion of the app simpler and easier to maintain.
JSON & HTTP. Again, this is another area of the app where I was better off leaving it to a library like OkHttp, Retrofit, and GSON. I wrote all the code that dealt with HTTP (built on HTTPURLConnection), JSON parsing, and serialization / deserialization. On one hand I learned a lot of what happens underneath the hood. On the other hand I spent a lot of time on parts of the app where my time should have been spent on something else.
Other things to browse the codebase for:
Integration with Google Cloud Messenger
Using the Material Design Support library for the navigation drawer
The episode downloaded
Cursors and CursorLoaders
Implementation of the Sonic library to altering the playback speed of audio in ExoPlayer
Aggregation of new episode / download notifications, built on a network of PendingIntents
My abstraction of image loading libraries, halfway through this project I switched from Picasso to Glide. Due to the fact that I abstracted this from the app, all of my code changes happened in one or two files despite the use of image loading in tons of places.
Tricky tricky SQL queries
I do have plans to make PremoFM functional again. If you build it, chances are you won’t get far because it requires the PremoFM backend and API. All of the XML parsing happened on the server. I have plans to port all of the XML retrieval and parsing logic to the app because I desperately want to use this app again. Clone it, fork it, and hack away at it to your hearts content.
ExoPlayer is an extensible, application level media player for Android apps. It’s an alternative to the high level Android MediaPlayer API. MediaPlayer is built on several low level media playing APIs like AudioTrack and MediaDRM. These low level APIs can also be used by developers to build your own media player with it’s own custom behavior. ExoPlayer is built on these low level APIs and it has the additional benefit of being open source. You don’t need to build your own media player, from scratch, to get the behavior you need. You can extend ExoPlayer instead.
ExoPlayer was created and is maintained by Google. Out of the box, it can play a wide range of audio and video formats such as:
Remember, ExoPlayer is open source, so it can, with some extension, decode and play any format, as long as you build the capability.
Just a Few ExoPlayer Basics & Components
The ExoPlayer class is the actual media player. It depends on a few other components for media loading, buffering, decoding, and track selection. When all of the required components are configured, your app will interact with the ExoPlayer class to control the playback of your media. You can register listeners with ExoPlayer to be notified of certain events like buffering or the conclusion of a track.
The MediaSource class is charged with controlling what media will be played and how it will be loaded. The MediaSource class is used directly by the ExoPlayer class. MediaSource enables a ton of different behaviors. For example, you can merge multiple MediaSource classes together to show video, along with captions or you can use multiple MediaSource classes to create playlists where the transitions between those sources are seamless (gapless).
There are several prebuilt MediaSource classes available out of the box in ExoPlayer to support many common use cases like playing normal media files or streaming DASH content from a remote server. Of course, you can implement your own to support your application’s use case.
The DataSource class provides samples of data to a MediaSource. These samples of data can originate from a file on the SD card, a resource in the assets directory, and even a remote server. You can use one of the prebuilt DataSource classes or build your own to read data in a way necessary to support your use case. For example, maybe your application will stream media on a company intranet. You can use a custom DataSource to define the rules and protocols that allow this to happen securely.
The TrackSelector class dictates which track is selected for rendering and playback.
The Renderer class decodes media and renders it. An example implementation is the MediaCodecAudioRenderer, which decodes audio data and renders it using several lower level ExoPlayer APIs.
The LoadControl class defines the buffering behavior of a particular MediaSource.
At this point, I know as much about ExoPlayer 2 as you do. I have some pretty extensive knowledge of ExoPlayer 1.X because I’ve used it on several Android projects that I’ve worked on. This series on ExoPlayer 2 will document my journey of learning about ExoPlayer 2 and upgrading an app to ExoPlayer 2 that is currently using ExoPlayer 1.5.9. I will probably make mistakes, but I hope this series will help a few other developers in their effort to implement ExoPlayer 2 in a real world app.
The app I will be using for demonstrating this upgrade is PremoFM. PremoFM is an open-source podcast player that I started building almost two years ago. The source code for the app is on GitHub (https://github.com/emuneee/premofm). I will be using a branch (https://github.com/emuneee/premofm/tree/exoplayer_2) for all of my ExoPlayer 2 upgrade work. I invite you to follow along. I’ll be back next week to discuss the structure of a typical audio playing app and how ExoPlayer fits in.