A summer of Android
By: Artem Sharypov
Tags: Android, Strava, Internships

About me:

I’m Artem, one of the Android interns at Strava in San Francisco during Summer 2019. I’m from Canada and have studied at the University of Manitoba for my Bachelors of Computer Science for the past four years.

Team I’m on:

During the summer I was on the Experience team, which works on the experience for athletes including improving app startup time, increasing upload and recording reliability, etc.

What was my project?

My main project for the summer was working on adding a way to upload FIT files into our current uploader service for Android. It sounds pretty straightforward — if the current service can handle JSON, don’t you only have to change parts to FIT and you’re done? No, because the original service wasn’t designed to support JSON and FIT uploads at the same time.

Why switch to FIT?

FIT files are Garmin’s proprietary file type that contains information about an activity. We want to switch to FIT because it would help reduce the failures that can come from uploading an activity. This includes failures due to a poor network connection — a FIT file in size is smaller than a JSON blob of the same activity meaning that it won’t need as good of a connection.

Why is uploading an activity hard?

Uploading an activity is hard because it’s more than uploading a FIT file, or a JSON blob. From a high level, it’s a 2 step process. The first step is the activity upload, and the second is when the server recreates the activity from the upload. The clients have to handle the success, failure, and error cases for both steps.

What does a successful upload look like?

Activity has been recorded and is written into the database. Upload Service starts and fetches activities to upload. For each activity to upload, convert it to either a FIT file or a JSON blob. Upload the activity in the FIT or JSON format. We receive an upload ID. Use the upload ID to poll the backend for the state of the activity being created. We receive an activity ID in the response once it’s created, then we delete the activity on the client and show a successful upload notification.

Why we aren’t using the current service:

We went with a completely new upload service, rewriting most of the logic used for uploading for several different reasons:

Current service in a nutshell

Testing the current service is hard, and requires hacky solutions to inject dependencies for the tests to work. It’s not easy to understand what happens in our upload flow since we use two different queues that are managed by two separate classes. Our service has a queue for uploads, and our sync looper has the other which is used for polling the status of the uploaded activity. It requires remembering intricate details on which queue is used for a specific state, and when it’ll be switched between the two. We wanted something that’s more flexible in terms of being able to upload either a FIT file or a JSON blob. It’s hard to introduce any design changes. We wanted to avoid the risk of introducing any errors to the current service from changing the current functionality to accommodate FIT files.

New upload service requirements:

We had a couple requirements for our new upload service:

Must be format agnostic, in the sense that we should be able to support both FIT file and JSON blob uploads JSON and FIT uploads should be independent Must be able to support different retry strategies in the case of failure

New upload service/flow:

Our new upload service and the upload flow now looks something like this: