Making money from an Android app in 2018 (part 2/3)

In this article series, I’ll talk about my experience making some solid beer money building and maintaining an Android app as a side project.

Nicolas Bridoux
10 min readAug 22, 2018

This is the second part in a series of blog post on making money from an Android app in 2018.

Analytics

This is what helps me to take better decisions. From the get-go I knew I had to have some sort of data to get a sense of how my app is used. I opted for Firebase analytics, it’s a free tool offered by Google that allows you to track a number of relevant metrics for your app.

It tells me how many users use my app daily (Daily Active Users), weekly (WAU), monthly (MAU). How long they stay in my app, and specifically on which screens they spend the most time. With that, I now know that people exercise a lot less during the weekends compared to weekdays. Also, people exercise the most between 6pm and 9pm.

That tool also allowed me to log custom events for user actions. For example I log an event when a user starts a timer and which type of timer it was — so I know which type of timer is the most used — , I log an event when a user dismisses the rating popup — only to see that a user dismisses it on average twice before giving the app a rating

Example of events, from left to right: event name, count, users

The above screenshot shows that the most used modes for my app are Tabata and For time, then comes Custom timers and EMOM, finally, AMRAP is last.

All of this gave me the data to prioritize the feature ideas I got. If the feature idea is kind of hard to implement and would not impact most of the users, I’d simply de-prioritize it from my list. Same thing if a feature request was too narrow, even if it was easy to add, I didn’t want to clutter my app with features that would add very little value (remember that 80/20 rule?).

Also if I’m thinking of removing or replacing something, I can see if it makes sense or not. Maybe I’m not using that feature myself but other people do.

All of this data is anonymized. It means I can’t know exactly who is generating which events. I know that someone in France is using my app, I know that X users clicked Y times on a specific button, I know that on average people spend 16 minutes in my app, but I don’t know who is doing what exactly.

Crash reporting

This is also an essential part of developing a high-quality app. You have to track the crashes your users might experience. The tricky thing with app stability is that your app can be used by a wide variety of devices that have different Android versions, screen sizes and configurations (languages, launchers etc.).

For this part, I decided to use Crashlytics. This tool is a well established mobile crash reporting library that now integrates with Firebase (the analytics part above) so you can see the crash reports in the Firebase console. Once integrated, the tool provides you with verbose Stacktraces (error messages) and the devices information where the crash happened to help you figure out where the issue comes from.

Example of a crash report I got recently 🙈

Nothing too specific about that, every time you make a new release you have to keep an eye on the crash reporting to see if the changes you made broke something or not. It helped me a lot to find bugs that I didn’t have with my own testing device and achieving 99+% of crash-free users.

Remote configuration

This is something “nice to have” in a modern app. It allows you to tweak the app without having your users to update it. You can use this to enable/disable features of the app, or quickly change some behaviors.

As an example, the prices are under a remote configuration, it means today I could change the pricing of my app without having to code anything.

An example of what remote configuration looks like

I could also play with the frequency of ads displayed. I would put maybe “show an add every 20 app launch”, then try to reduce that number to 5 and see how users would react and how the revenue would be affected and so on.

Advanced development tools

In addition to analytics, crash reporting, and remote configuration, I tried a few other tools to help me raise the quality bar of my app. Some of them turned out to be interesting:

Leak canary: This tool allowed me to spot memory leaks in my app. You can see a memory leak as a waste of resource. When an app is running it uses resources (memory, CPU..) from your phone, when your app is done with its work then those resources are not used anymore and are released. In some cases it can happen that by mistake you can “hold” some memory and never allow the app to release it, this is a memory leak. Most of the time, there are no big issues as your phone has a lot of memory, but in some cases, it can lead to your app simply being killed by the system (e.g crash randomly). Therefore, to be on the safe side, this tool allowed me to prevent memory leaks (and see that the library I use for ads has a memory leak itself that hasn’t been fixed for years).

Nanoscope: This new tool (developed by the folks at Uber) allowed me to profile my code and trace the execution time of my app. In other words, I could see where my app took the most time to execute on the UI Thread — The UI Thread is basically where visual things happen in your app, if this Thread gets too much work, your app will be laggy / freeze — This helped me to fix some rookie mistakes such as blocking database operations on the UI Thread.

StrictMode: This tool comes directly with the Android Framework. It pretty much follows the idea behind Nanoscope but acts more like a “safeguard”. You can set rules such as “no disk read/writes operations on the UI Thread” (disk read/writes are supposed to be long-running operations, therefore you want to avoid having them on the UI Thread so your app stays responsive). If you enable StrictMode and still perform a disk writes on the UI Thread, it will force your app to crash, thus bringing the issue to your attention (fail fast). I used this tool but it didn’t integrate well with the ads library I use, I had to ignore a few of the warning I’ve gotten. Overall, it added an extra layer of safety regarding slow operations on the main thread.

Delighting users

All of these tools are about giving a nice experience to the users. Some of those things are far from necessary in today’s world (because devices are getting more powerful, more memory etc.) but they still matter to some users. Besides that, I gave some consideration to delight users.

Increasing app performance

A recent thing I started to have a look at is the app performance itself. There is a nice tool on Android that allows you to easily Inspect GPU rendering speed, it shows which phases of the rendering pipeline take the most time. I’m not going to much into details in this article as I wrote a tiny article about chasing frame drops on android, but it basically improve the fluidity and battery usage of your app.

This resulted in less dropped frames (jank) on the Clock view of the app, which is where users spend 84% of their time in my app. This is huge.

Listening to user feedback

Believe it or not, users care about those tiny things.

Example of updated review after I fixed a small issue

This user noticed a small delay when switching from an interval to another and reflected it in the app ratings. I had a look at the issue and managed to find where it was from and pushed an update. The user updated his review afterward, this was such an encouraging win for the beginning of the app.

Features discovery

Another great example in my opinion on delighting users is to help them discover your app.

All over my app, I added a few tutorials on key actions to help them understand what the app has to offer. I tried to put them in strategic places and keep them non-intrusive and relevant to the context (show them they can launch a workout in one click only when they land the workout detail screen..).

I think this makes the onboarding process seamless for users that are “just giving a look” at the app instead of bothering them with an onboarding slider for example.

App translations

Users prefer to use an app in their native language. Nowadays a lot of people speak English but in some countries, people don’t speak really good English and simply won’t use an app if it’s not in their native language.

To help translate my app I added a button “help to translate” in the app. Users would be redirected to a Google form where they could give me their email & the language they want to help with.

From there I have created a Google Sheet with all the translations of the app. Surprisingly enough, users that really enjoy the app don’t mind putting an extra effort to help improve it and to translate it.

This effort particularly helped also in terms of growth, the store description and screenshot had also gotten localized. I got a lot more users from Italy, Spain, and Russia using my app and giving reviews after it was translated.

Build flavors

All along the development phase, I needed to have specific “variants” (different versions of the app with some tweaks) only for debugging or testing purposes. Gradle build system allows you to create Build flavors to accommodate those differences and generate different apps.

In my app I have 4 build variants:

  • debug: This is the one I use when I work on the app, it has a specific package name and fake ads.
  • screenshots: Very similar to the debug one, this version of the app is meant to be used for the store screenshots, therefore, it doesn’t include ads.
  • inAppBilling: This variant is almost my release version of the app. Everything is like in release mode, the package name is the same as used on the Play Store to be able to test in-app purchases but the debug mode is enabled in case I find an issue with this version I can debug it. Also, it contains fake ads.
  • release: This variant is the version at the destination of the Play Store. It contains real ads, disable debugging and has the good package name.

Having distinct variants allows me to adapt to different use cases, debugging, taking screenshots, testing my in-app billing and having a clean release APK. Also having different package names for each build variants is very convenient as I can have 3 versions of my app installed on a single device, with their own database.

Release process

On Android, the place where you submit your app at the destination to the Google Play Store is called the Google Play Console. It is a great tool for managing the release process on Android.

It has a different set of tracks where you can release your app to particular set of users.

Firstly I use the Open track (open beta testing) when I release a new version of my app. Basically, users can opt-in to beta testing into your app and will receive an update when you push a new build to this channel (I’ve put a “become a beta tester” button in-app to increase the number of people opting-in to the beta track).

Once I’m confident I didn’t introduce any major bug. I can use the phased rollout of the production track. The phased rollout allows you to release an update to a small percentage of your active users first so you can spot any bugs early on. I usually go for 25% of my users, then after a day, I update to 50 or 100% depending on my confidence.

It’s worth noting that it doesn’t take more than half an hour before the moment you upload a new build to the Google Play Console and the moment it is available to your users (there is now even an Internal test track that is supposed to be instant). This is really nice. I once introduced a major bug that would crash the app every time on app open (but only for the release variant, so I didn’t spot the bug when working on the debug variant), I noticed it 1h later and was able to push a new build in the next hour.

For every update, I also force myself to write update notes in both English and French that user can read in-app in a specific location or on the Play Store to stay informed about what exactly changed.

Without further ado, let’s dive into part 3 which will talk about the financial aspect of all this.

--

--