Gfycat for Android

I think Gfycat is doing some amazing work with their service - I don't mind gifs as a visual medium but it has always bothered me how large/slow they are to load. Gfycat takes those gigantic gifs and compresses them to a more convenient video format, so it takes up less bandwidth and runs smoother.

Gfycat also provides an API, and so I've just finished my latest side project, Gfycat for Android.

As per usual with my side projects, I tried out some new stuff with this app. This time the app was my first fully fleshed-out foray into RxJava. If you want an introduction to RxJava check out the wiki. RxJava is hard to grok; I must admit I don't have a full grasp on it myself yet. But here's a case study of how it helped me.

When you want to use the Gfycat API, there are a few steps involved:

  1. Send the gif's URL to see if it's been converted already.
  2. If not converted, send in the gif's URL to convert it.
  3. Request the URL of the output video file from Gfycat.
  4. Load up the TextureView.
  5. Initialize the MediaPlayer with the TextureView and URL.

There's three problems that normal iterative code has with this setup:

  1. The path is not linear. If the gif has already been converted, you can skip step 2 and go straight to step 3. If the URL was already from Gfycat, you can skip straight to step 3!

  2. MediaPlayer starts asynchronously. Querying Gfycat's API is asynchronous as well. You could do it iteratively, losing out on concurrent speed but gaining some sanity. Or you could try to track their separate state, which is a pain.

  3. It can fail every step along the way. It's a real hassle having to catch all those exceptions.

These are not insurmountable problems, but they are a pain to handle and this is a simple app. What RxJava gave me are solutions to each of these problems (seen in MainActivity):

  1. I can use a chain of operators to define the flow of data in my app. RxJava's flatMap() allows me to compose data in such a way as to skip steps if they are unnecessary, but still execute them if they are. Not only is it easier to write, I feel it's easier to read afterwards because you can see the flow of data in the code (no callback hell).

  2. I use combineLatest() to keep track of the state of TextureView and Gfycat separately. Instead of having to track state myself, it's a couple lines of code to combine their state and only spin up the MediaPlayer when ready.

  3. All of the above logic is represented in one sequence, so I can stick one onError() subscription at the end of it all and handle all exceptions in one place. This is great because the app purposefully uses the same error dialog for all states.

You can find the source code here if you want to check it out for yourself.