Sat, 03 Dec 2016
So "version 2" of my Wallpapers app for Android went out on June 30th. I then looked to make improvements. I put the category name on top of the category page. I adjusted text size by the screen dpi display. I removed image margins for image details. I also did some UTF-8 fixes, as I wanted to start moving into the international, non-English exclusive market. These changes done I release "version 3" (technically 7) on July 23rd.
Continuing on non-English languages, I then did many more Android side and server side changes to handle other languages. I paid to have the app translated into Spanish, French and German. Many of the pictures already had multi-language blurbs and/or tags. I upgraded the gradle version. I fixed the button and button text size. I added more licenses for the pictures being used. I added functionality so categories could survive screen rotation like non-category grids. Google seemed to be sidelining Google Analytics and pushing Firebase, so I tore Analytics out and put some Firebase in. And then - release of "version 4" (8, technically) on September 16th.
I had done the foreign languages because it seemed as if it was time to expand beyond English-speaking countries. People used the app, rated it well, came back to it to check for new wallpapers. I thought I had done enough QA on it. So three language translations were paid for, and on push-out of "version 4", I began doing heavier paid promotion of the app.
Oops. More people meant more usage which meant more bugs exposed. I got a one rating on September 21st and another 1 rating on September 24th. I tried to figure out what was going wrong. It was hard to pin down specifically who the one ratings were in my web logs, but I had what looked like good candidates. Both had loaded the JSON pages but never displayed the images. I looked through the code and it looked like I could have made an error somewhere. I also saw from the logs others were loading two JSONs and that was it, but didn't give any app ratings (meaning they were probably unhappy but didn't know it). So I wound down the paid promotion somewhat while I looked for what was wrong.
While this was not the only thing I did for the past two months, it is what I was working on in the past two months with regards to the Android (client) side of this app.
One blind alley I went into was putting debugging code in to send a bugging message to the server. First I tried Firebase which didn't work well. Then I rolled my own.
What I should have done to speed things up is go over the code line by line and see if it made sense. I should have modified my dev server scripts to slow down responses, to a slowness rate I don't have during regular QA, but which people who have a slower connection in some countries have.
I had desired to architect the app nicely, but in just getting bit by bit working, it had become messy. What I had to understand was the Android classes, namely Activity and Fragment, and their modern usage in Android, for example AppCompatActivity over Activity. I do things the modern way. I need to understand Activities, Fragments, BroadcastReceivers, AsyncTasks and so on. I need to understand their lifecycle, how they respond to screen rotations. How they respond to people hitting the home button, back button, or UI buttons. How they communicate from element to element, say, Fragment to Activity, or vice versa. I also have to anticipate that some people will load JSON slower than me, and this sort of thing, which means if I do not design responsiveness and concurrency correctly, and do not QA with this in mind, I can miss it.
Any how, the app was rearchitected. Everything hangs off an Activity mostly. Not exactly object oriented ideal, but even the best Android programmers seem to complain about context god objects, fragments and so forth. I have a fairly light fragment hanging off the Activity. I also have a class which is the main data structure which hangs off the Activity. There are two instances of it, associated with the two fragments, but due to the quirks of Android it is easier to have them associate with the Activity than the fragments they are associated with. Off this data structure is an AsyncTask that hits the web API and expands the size of the data in the data structure. I have other things going on, but that's the main architecture - an Activity with three Fragment instances hanging off it, plus two data structure instances, each with AsyncTask sub-classes that grab more data.
In the investigation of who was having problems, I could not always tell who was who by device type or IP. So I started having devices send an instance ID to the web API. But it can take a few seconds for an instance ID to generate. So I do one JSON grab, have the image library (Android UIL) grab the images it shows, generate an ID and then grab the locations of more images. I want people to get a quick response before the delay of an instance ID creation, even if I have to match people up later on the server-side. The main thing is seeing who is having problems so I can fix it.
For screen rotation, I had a Parcelable data structure object in another Parcelable data structure at some point. This was too complex for the system, or me, so I just made the top instance Parcelable and pushed the information up to it.
Things have been moved around enough in this, enough times that it seems to work but looks a little sloppy. So I will clean it up and make it look nicer. It took two months to get to this point though, so if it's working I will be taking a little breather, I spent more time rewriting this than I had wanted to.
I started writing this app in early March. I wanted to write an app that was a pretty vanilla Android app with no fancy NDK/JNI stuff. I also wanted one that could be popular and possibly slightly lucrative. Also one which was not something people could knock out in a few weeks, but which would not take forever either. After all, I have to build apps of this type before I can handle the schedule estimating and size of yet more complex apps. One thing doing this app has taught me is a better idea of scheduling Android projects, or even programming projects in general. Also about the care I need to put into a complex program with each logical piece. Also to make accommodations for Android oddities.
I'm still looking over the code and looking out for user problems. Hopefully the main problems have gone away. The app has 21 5 star ratings, 9 4 star ratings, and 2 1 star ratings. The 1 stars both seemed to have the problem where the app wasn't working. 34% of downloaders are active devices. This is higher for some other apps, but this one is more of an entertainment one. It also has 13% user retention into month 2 in terms of active use. If I update wallpapers more frequently that will go up. So hopefully I fixed the problem that had come up.