Posts from the ‘Uncategorized’ Category
Publishing Android apps in the early years
October 3, 2016
I started on my first Android app two months after the platform’s 1.0 launch. I felt late to the party. Round one of Google’s Android Development Challenge had already concluded, and the five million dollar prize pool was already rewarded.
I figured it was still worth a shot. A colleague had made a bunch of money off of iPhone apps during the initial gold rush, including a few thousand dollars on a well-crafted tic-tac-toe app. I figured the same would happen on Android, and hell, I already knew Java.
When I got my Android Dev Phone 1 in December, 2008, I noted about 140 apps in the Android Market (the precursor to Google Play). The “Cards and Casino” section was quite light on content, and I had just spent two years working on poker and slot machine software.
There was no clear path to making any money at that point. The Market did not support paid apps yet, and there was no ad library. I figured the matter would be settled soon enough, and so another colleague (graphic designer) and I set to work on a simple slot machine game.
Documentation was reasonable, if a bit sparse. I worked off the original Lunar Lander example code. It had a lifecycle-related bug, which stayed open on the Android bug tracker for 6 years before someone finally marked it obsolete. I had considerable difficulty maintaining 30+ frames per second while still updating the view when I intended to. I remember being desperate enough that I tried calling draw() recursively. It worked great for a second or two! Perhaps the biggest technical achievement was a “pullable” lever that tracked the user’s finger.
Within about a month, we were ready to launch Beat The Joker Slots. We were the twelfth entry in the “Cards and Casino” category. AdMob had just published its initial SDK, and I set it up to refresh an ad every ten spins. The immediate results were striking, though it slowed down pretty quickly, per this email I sent to my collaborator:
“So in the first hour, it made $9. After 2 hours, it was at $15. Then it slowed down a bunch – 12 hours later, it has made $20. I blame this somewhat on admob having a bunch of repetitive ads, and also, they seem to have ran out of unique ads to serve to the user base, the fill rate (how often they provide an add when I ask for one) fell from 99% to 78%. However, we’ve already had 10000 impressions, which shows we really are getting decent volume. Latest count shows 1200 downloads, with a rating of 3.75 stars, which is pretty great for a game that I personally would get bored of in under 3 minutes.”
We settled into a pretty nice place, though. Beat The Joker peaked at #4 in its category. Along with a reskin (Zombie Jackpot Madness), we generated about $300 a month from Admob (and a few other advertising SDKs) for a solid three years. The two apps ended up with over 650,000 downloads combined. It’s pretty remarkable, considering how basic those games are. If I published them today, I doubt I could recoup the $25 I paid to open the developer account.
I didn’t know all that much about advertising then, but I saw a list of most valuable keywords on AdSense, and “mortgage” was one of them. I thus conspired to create Real Estate Droid, a front end for a half dozen real estate APIs I found. It included an auto-updating “show me the nearest home for sale” feature, which I thought was pretty innovative in early 2009. I included a mortgage aggregator’s quotes for a modest fee, and displayed Adsense ads in a WebView to get that valuable real estate advertising. The UI was completely un-styled and generally ugly. I didn’t have access to MLS systems, the most comprehensive database of US real estate for sale. However, it was the second real estate app in the Market, and hit 100,000 downloads in relatively short order.
Network requests were tougher back then. Real Estate Droid launched before Android Cupcake, the release that included AsyncTask. Developers often sneer at AsyncTask nowadays, and perhaps rightfully so. Back then, it was a godsend. Before AsyncTask, I needed to deal with Handlers directly for network requests, and there wasn’t a lot of advice online on how to get it right. (Never mind processing a SOAP API… I think that’s a pain even today.)
Updating a ListView asynchronously was even worse. List items get recycled as they move off screen, and if you’re not careful, you may end up overwriting a list item’s content when a response comes in for a request you issued during that item’s previous lifetime. The official Android Developer Blog addressed this issue a year later, but by then, I had already spent way more than my fair share of time figuring it out on my own.
Adsense served very valuable ads for a few weeks before I got banned. Six months later, I heard from an Adsense representative who personally invited me to integrate their beta Android SDK. Go figure. I learned that they had been upset that I was showing web ads in an Android app, but that was no longer a concern. I was back in business!
It got better. Admob and Adsense would both piloting new ad products from time to time, and they were both willing to make some financial guarantees to secure my participation. From my perspective, it seemed crazy – Admob had been acquired by Google, and it was as if they were competing against each other. From their perspective, I can only imagine that my royalties amounted to rounding error.
That was the high water mark for my app empire. If I had built a multi-game casino app, or a real estate app with a top-notch UX and partnerships with Realtors, I might’ve been able to keep growing. Instead, I focused on my day job, and better apps slowly ate my lunch. After a few years, API dependencies shut down and I unpublished Real Estate Droid.
Regardless, I consider the effort a huge success. My app income translated to an attractive hourly rate. When Android later became popular enough that every business was saying “Oh, and we need an Android app, too!” (2010), I became the lead mobile developer at my day job. In 2012, I joined the Android team at Google and became one of the founding engineers on the Project Fi. I was always a tiny bit starstruck whenever I ended up on an email thread with the engineers whose names I remembered from early Android. I continue to work at Google, though lately I’ve shifted my focus to Machine Learning. TensorFlow feels a lot like Android 1.0 did. Everything feels new, full of potential, and occasionally, a bit of a mess.
I’m going to work for Google
September 17, 2012
A few minutes ago, I accepted an offer to be a software engineer at Google. As you might suspect, I’ll be doing Android development.
With that decision, I am no longer available for contract work. I plan to maintain my blog with roughly the same irregular posting frequency as over the last 9 months.
This was taken at the Googleplex in Mountain View, California. Note the little green Android guy in front of the giant statue – Jenny knitted him for me!
Sample Fragment code available on GitHub
September 4, 2012
Tonight, I put together a simple Android project to get a better grip on the Fragment lifecycle as it relates to the Activity lifecycle, and to do some simple Fragment manipulation. I have used Fragments in past projects, but never from scratch, and setting this up was pretty illuminating. I annotated the source with explanations and relevant Stack Overflow links and pushed the project up to GitHub. The resulting project now lies here: https://github.com/goat000/FragmentDemo
Some of my basic findings:
- You can’t remove Fragments that were declared within your Activity’s layout file. You can only remove Fragments that were added programmatically.
- If you are going to add a fragment programmatically, make sure to inflate its view with inflater.inflate(R.layout.test_fragment, container, false); If you skip the third parameter, you’ll get IllegalStateExceptions that say “The specified child already has a parent.” It’s rather annoying, as the stack trace won’t lead back into your code.
- onActivityCreated is called for a new Fragment even if its Activity was created a looong time ago, and it’s called after onAttach. This seems peculiar – how do we attach to an Activity before it’s created? However, it doesn’t seem to hurt anything.
- Fragments that are added programmatically are retained across configuration changes, like rotating the screen.
- Fragments have their own onSaveInstanceState but no onRestoreInstanceState. You can restore the saved Bandle in onCreate, onCreateView, or onActivityCreated.
Please do let me know if you run into problems with this.
Android has a sense of humor
June 2, 2012
According to Google, “Each major release of Android has a method or constant whose name is amusing or just silly.” Here are three I’ve come across:
For some time now, the Eclipse ADT Plugin has contained a logcat tab. This provides a (mostly) nicer way to view logs than executing “adb logcat” at the command line. This was not good enough for one developer, who opened an issue in Android’s bug tracker with the following text:
I would like the LogCat icon to be modified to a cat.
It would entertain me while developing and debugging.
For those not in the know, NyanCat gained a great deal of popularity on Youtube last year, and the original video now has 75 million views. Google came through, and as the first developers downloaded ADT version 14.0.0, they noticed the new, nyancat-inspired icon.
Yes, there’s a method in the Android SDK that will tell you if the current user is a monkey. It’s not quite what you think. Android includes a testing tool called “UI/Application Exerciser Monkey” that generates random interactions with your app as a sort of idiot test. I’ve found program defects with this tool that I don’t think I ever would have come across otherwise. isUserAMonkey() lets you know if “the user interface is currently being messed with by a monkey.”
In Android 2.2 (FroYo), the Android team introduced a new logging level higher than the previously-highest level, “e” (for error). According to the SDK documentation, WTF stands for “What a Terrible Failure” and is reserved for reality checks for conditions that should never ever happen. Depending on one’s development settings, a WTF() message may terminate an app immediately after it is output to the log.
I’ve always appreciated running into these. I know there are more (see fyiWillBeAdvancedByHostKThx) but I’m sure I’m missing a lot. Does anyone else know any good jokes hiding inside the Android SDK?
Where to hold Global application state in Android apps
May 9, 2012
I recently started working on a project with savvy apps and we came to a discussion about where to handle Global application state – a bit of information that you need to access pretty much everywhere. Here are the options I’ve thought of, and the tradeoffs involved.
Extend the Application Class
You can extend the Application class and add your own fields to the subclass. You specify your class in your manifest file with the android:name attribute without your application node. Then any context (e.g. Activity or Service) that wants to get to this information can call
Context.getApplicationContext() to get your Application object.
This is a reasonable way to store state that you’re okay with losing when the Application is destroyed. The documentation page for Application mildly discourages this use:
“There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a
Contextwhich internally uses
Context.getApplicationContext()when first constructing the singleton.”
Fair enough, that brings to:
Plain-Old Globals Class
There’s really nothing wrong with have a class with a bunch of static fields that you get and set as needed. Just don’t hold onto any Contexts, or any objects that hold a reference to a context, like a View. Again, anything you set here will disappear when your Application is destroyed.
This is the way to go when you need information to persist across your app being destroyed and re-launched. SharedPreferences are a simple key-value store that you can access from anywhere that you have access to a Context. There’s a straightforward intro on the Android Developers site. Just make sure you don’t store things here that you DON’T want to persist indefinitely.
Avoid Global Application State
You should always cringe a little when you consider adding a “global” anything. It has its place, but it breaks encapsulation and adds complexity to your code – there’s always one more bit of information the code your working with COULD be dealing with, so there’s one more thing you need to keep in your head. Per the famous Mr. McConnell, we should always endeavor to structure our code such that working on any given chunk of it requires keeping track of as few things as possible.
The alternative to application state is often passing extras within the Intents that launch activities. If you’re writing a newspaper reader, you probably don’t need to store the current newspaper in a globally-accessible field. Rather, when the user clicks a newpaper to see it’s latest headlines, you pass in the newspaper ID as an Intent extra. When the user clicks a headline to see the article, you again pass along the newspaper in an Intent. The downside is that you have to add and read that bit of data to all Intents that care. The upside is that you have a better idea of what parts of your code are using what data. I generally prefer passing data around this way when I can.
Interviewing Programmers (Or: When FizzBuzz is asking too much)
April 22, 2012
FizzBuzz entered the software development lexicon in 2007, when Imran Ghory wrote a post about a simple programming problem that can weed out those who just aren’t very good when it comes down to actually writing code. The task: “Write a program that prints the numbers from 1 to 100. But for multiples of three print ‘Fizz’ instead of the number and for the multiples of five print ‘Buzz’. For numbers which are multiples of both three and five print ‘FizzBuz’.” If you can’t write that out in a few minutes, I don’t want to pay you to write code for me. As Mr. Ghory mentions, a depressing percentage of candidates can’t do this in a timely fashion.
This doesn’t surprise me. Thanks in no small part to this classic post from Joel Spolsky, I believe very strongly in making candidates program in their interviews. At my last job, my main role on interview day was to prompt candidates to code on the spot and help them along just enough to keep them moving when they got stuck. I always started them out with something even simpler than the FizzBuzz problem: “Print out all of the elements in this List” (it’s a Java interview). Most people got through it very quickly, and perhaps we’d go from there to a brief exchange about exception handling or the behavior of toString() and why every object will have that method defined. For some, it was problematic, but I’d give them a chance to print out elements of an array, or to do something similar in the language of their choice, and at least that would keep us moving. However, about 15% of my interviewees crashed and burned here. These people might possibly write decent code, when given an IDE with code completion hints, but if that level of coding isn’t fully burned in one’s brain after doing even a fair amount of coding in college, I don’t want to take my chances that said candidate will ever reach a barely-acceptable level of productivity.
These “immediate no” candidates had already convinced HR that they had sufficient qualifications for the position, and had already provided answers to specific technical questions I asked them on the phone. If not for the coding exercises, we would have made a couple of seriously damaging hiring mistakes.
Here’s my preferred interview structure for anyone that’s going to spend significant time mucking about in my code base. This assumes a position that will be doing web application development, but also a helping of “mad scientist” work – the kind of work that goes into building a revenue-optimizing ad platform or a search engine.
- Head of the department asks the soft questions
- One coder asks questions about high-level software design issues and specifics of relevant platforms
- One coder asks questions related to algorithms and data structures
- One coder (or perhaps two) asks the candidate to work through some programming problems
The specifics should be modified for the level of the position being discussed, and it’s not so important how the questioning is split up among interviewers. For recent grads, I’m not too troubled if they’re a blank slate when it comes to design patterns. For senior candidates, that’s not okay. (Side note: I don’t care so much if someone doesn’t know the names of patterns or can’t enumerate a bunch of them. I’m more interested in seeing if they can come up with a decent high-level design for a problem that a popular design pattern solves well.)
Do me a favor – tell me about your interview process, your worst interviews, or what’s wrong with my suggested interview schedule.
P.S. Bored? Check out this solution to the FizzBuzz problem. It has no branches and involves the number 19142723.
Improve your software developer job post
March 5, 2012
A few weeks, a hiring manager on Reddit asked the readers of cscareerquestions were they look for jobs, and expressed frustration with the developer candidates HR sent his way. The developers sent up a few guesses about why the hiring process was going badly for him. Maybe your HR people are filtering based on simple keyword searches because they have no idea what the position actually does. Maybe you’re doomed because you’re looking for C++ developers in a city where the only other C++ work (high frequency trading) pays extremely well. Maybe it’s your job description.
He posted the job description. It was definitely the job description.
Here’s an excerpt:
“The software developer’s role is to design, code, test, and analyze software programs and applications. This includes researching, designing, documenting, and modifying software specifications throughout the production lifecycle. The software developer will also analyze and amend software errors in a timely and accurate fashion and provide status reports where required. ESSENTIAL FUNCTIONS: Strategy & Planning • Assist other developers, analysts, and designers in conceptualizing and developing new software programs and applications. • Plan phases of the software development life cycle (SDLC) for a variety of projects. • Assist in the preparation and documentation of software requirements and specifications. • Research and document requirements of software users. Acquisition & Deployment • Conduct research on emerging application development software products, languages, and standards in support of procurement and development efforts. • Recommend, schedule, and perform software improvements and upgrades. blah blah blah blah BLAH BLAH BLAH BLAH BLAH BLAH”
Yes, it was formatted just like that, too. But formatting aside, what’s wrong with this? I bet it describes the position accurately. It also describes every other software development position on the planet. (Don’t believe me? Google any of the sentences in the excerpt above and see how many postings use those exact words.) This posting went on for another hundred words or so before it finally mentioned ANYTHING specific to the position. 3 characters, “C++”.
Don’t put anything in the job description that I already know! Solid candidates aren’t interested in reading a description of a what a software engineer is. They want to know what the job is like and why yours is better than the others they are considering. Drop the bit about modifying software to meet business requirements, or the necessity of working well individually or in a team. Instead, tell me:
What project will I work on? Is it fun? Will it make a positive difference in people’s lives? What will I learn while I’m doing it?
Is there any proof that the existing team is any good? Maybe you have a nifty product already in the Market, or your lead dev speaks at conferences.
Are you willing to pay to get the best coders? If so, speak up! N.B. The only thing “Market” or “Competitive” suggests is that you don’t pay a top-end compensation package. It transmits no other useful information.
Does the hiring manager have actual technical knowledge?
Is there beer in the fridge? Do people like each other enough to hang out after work?
Does your company have any personality? Can you break through the Office Space-style language enough to show that a thinking human being wrote this, and a thinking human being will read my resume?
If you don’t know the answer to those questions, go back and talk to the development team. Maybe even ask the junior ones, the ones who aren’t yet fluent in corporate-speak. Ask them why they work at your company. If they have a decent answer, that’s your job post. If they don’t, you’ve got a much bigger problem.
Quick case study: Advertising an Android app with Admob
February 7, 2012
In my last post, I discussed the necessity of marketing your app if you want it to be noticed. Long story short, some apps take off on their own, but not many. You can do a lot for free (promote on social media channels, your website, submit to review sites, etc.), but you may also consider paying for exposure. There are a zillion options, up to newspaper and television advertising, but you should look at in-app advertising networks first. They target only users that have the devices you support, and a single click can take your potential users to your app’s download page.
Within advertising networks, you still have a zillion options. If you plan on putting substantial marketing money behind your app, you should try several to see what nets you the best returns. You can pay per impression, per click, or per download. Most ads will be banner ads shown inside another app, around 320×50 on a 480×320 screen, or a size scaled to match on higher-resolution screens. You generally won’t be able to buy for a specific keyword. You will often be able to choose between text or image ads. You might be able to buy full-screen interstitial ads, and you can even buy ads that appear as notifications in the user’s status bar. Of all those options, the only one I recommend against is the last one – notification ads. Users HATE them.
I recently ran a quick test on my newest app, Hoofit?, to see what results I could expect from an AdMob campaign. I had run some small tests a few years ago that came in around $2.00-$2.50 USD per download, which was not reasonable for an app that made about $.03 of advertising revenue per user. Since then, there has been one major improvement to advertising on Android – it’s now possible to link directly to an app rather than to search results with only that app included, so the downloads have less friction. I figured my cost per download would be a little lower this time, but I didn’t expect to find a good strategy, just a good story.
I ran two campaigns, one with a text+icon ad, one with a single image banner ad. Text+icon ads have a minimum bid of $.05*, and image banners have a minimum bid of $.15. As I result, I expected the text ads to do better, but I was ready to be surprised. Here are the results:
Text ad: $10 spent, 38,856 impressions, 201 clicks, 0.52% Click-Through Rate, 3 installs, $3.33 per install
Image Ad: $36.16 spent, 282,405 impressions, 257 clicks, 0.09% Click-Through Rate, 3 installs, $12.05 per install
Look at the difference in click-through rate. I attribute this to the information contained in the ads. The text ad’s copy was “Free Walkable Neighborhoods App.” So it told people what the app was about, and contained the magic word “Free.” On the other hand, the image ad was just the app name “Hoofit?” with a logo. In one sense, the CTR really doesn’t matter – I’m paying per click, and AdMob had plenty of inventory it was willing to give me, even for an ad with a terrible CTR. On the other, with a 0.09% CTR, I suspect a fair percentage of clicks were purely accidental, and that certainly doesn’t help conversion rates.
Overall, I’m disappointed that my best attempt of two cost $3.33 per install. I imagine I could do better by trying multiple different ad copies as well as multiple different landing pages (changing my app’s description to better catch people’s eyes). My conversion numbers would also be higher if my app appealed to a larger slice of all app users.
My advice to anyone else setting up a self-serve AdMob campaign:
- Unless you can really wow people with a 320×50 image (perhaps with a gorgeous game screenshot come to mind), stick to the (67% cheaper) text ads rather than images ads.
- Write your ad copy with conversions in mind, not clicks. Shade your ad copy slightly towards describing what the app does over click-bait words (new, improved). AdMob will favor high CTR ads over low CTR ads for the same bid, but they also have plenty of inventory available for low-CTR ads at this point. Try to keep your CTR over, say, .2%, in order to keep down the percent of clicks that are outright mistakes, but don’t worry about it too much after that. One exception: if you are running a very large campaign ($1000+ per day), you may need to adjust to favor CTR in order to get your desired reach, or you might just up your bid per click a bit.
- Try out different ads and different app descriptions to optimize your yield
- Make sure to try out pay-per-download services like GetJar and compare your results. In my experience, you can get downloads there for less than $.50 each. The downside is that the downloads come from outside the Android Market, so they don’t contribute to your ranking within the Market
- Make sure you’ve got a great app before you start spending money to market it
*The minimum bid is $.03 if you don’t target specific countries. My app only has data for 4 countries, so I had to target them specifically in my campaign
Modernizing an old app
January 8, 2012
This week, I spent some time on an update to Beat The Joker slots, the first app I developed. This app has been quite successful, with over 400,000 downloads to date, but it was developed at a time when there was only one version of Android to worry about (1.0) and one device available (the T-Mobile G1 and it’s developer-only variant). Knowing that, I originally developed an app that depended on having a 480×320 screen.
As more devices popped up, the Android team introduced a scheme for handling different screen sizes and densities in Android 1.6, and they also provided some quick and dirty compatibility scheme to scale apps that weren’t designed with screen adaptability in mind. Beat The Joker was my first experience with the Android SDK, and the code is pretty ugly, so I used that compatibility scheme for some time rather than diving back into my own mess. The game has remained playable, but its layout has been less than ideal.
I had several goals for this release. I wanted to update the Admob library to the latest latest, which will let Admob fill unused space with Adsense ads, hopefully increasing my revenue a little bit. I wanted to use a more proper layout scheme so that I can make use of wide screens, moving ads out of the playing area and into empty space where possible. I also wanted to clean up the code a bit in case I decide to integrate this in a future, more-ambitious casino app.
I’ve managed to accomplish these goals, with the help of a few coding rules of thumb:
- Don’t use AbsoluteLayout. If you must specify pixel-perfect locations, use margins in FrameLayout or RelativeLayout, but still specify elements positions relative to each other as much as possible.
- Use dp (density-independent pixels) instead of px (hardware pixels) in layout files. Density-independent pixels correspond to 1/160 of an inch. It’s not exactly that, because devices with slightly different densities are grouped into the same family, but it’s much better than not scaling at all.
- Always try to specify view positions in XML rather than in Java code. Converting Java values to density-independent pixels uglies up the code, and it’s harder to track down what value changes what position.
Beat The Joker still has a ways to go to be fully modernized. The biggest issue now is having only one set of graphics to work with for all screen densities, so the game doesn’t looked as good on high-density screens as it could. I’ll have to get my designer to create images at different scales in order to address that.