AsyncTasks in Parallel

Anytime you need to do a deep dive into Android’s framework code, you are bound to learn a lot.  I had a problem where album art wasn’t being updated because I thought an AsyncTask was blocked.  Turns out that was not the problem, it was a Picasso misconfiguration issue (more on this later).  My journey led to take a look at the source code for AsyncTask.  This is where I discovered, that the Android framework can execute multiple AsyncTasks in parallel for a given process.

I create two classes that subclass AsyncTask, called TransmitNameTask & SaveNameTask respectively.  You would normally execute them like:

TransmitNameTask transmitTask = new TransmitNameTask();  

transmit.execute(…);

SaveNameTask saveTask = new SaveNameTask();  

saveTask.execute(…);

Assuming you execute TransmitNameTask, then SaveNameTask, these AsyncTasks execute in that order, in serial.  Beginning way back in Android 3.0 “Honeycomb”, Google introduced AsyncTask.THREAD_POOL_EXECUTOR.

Order of execution

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

If I wanted to run the TransmitNameTask and SaveNameTask concurrently, I’d execute them like:

TransmitNameTask transmitTask = new TransmitNameTask();  

transmit.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, …);

SaveNameTask saveTask = new SaveNameTask();  

saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, …);

Executing in this fashion, if TransmitNameTask is waiting on network connectivity (or it was able to connect, but is experiencing slow service), SaveNameTask is not blocked.

How many AsyncTasks can you execute in parallel?  AsyncTask is built on Java’s ExecutorService.  Looking into the AsyncTask.java source code:

Core Pool Size (CORE_POOL_SIZE) (# of threads in the thread pool)  = CPU Count + 1

Maximum Pool Size (MAXIMUM_POOL_SIZE) (max # of thread in the thread pool) = CPU Count * 2 +1

Note: Core Pool Size is represents the number of threads created when a Runnable is submitted to the ThreadPoolExecutor and the queue isn’t full.  If the queue is full, the ThreadPoolExecutor will create more threads to go into the pool until the pool reaches the Maximum Pool Size.

This means a modern smartphone with a 4-core processor will have a Core Pool Size of 5 and Maximum Pool Size of 9, meaning it can execute up to 9 threads in parallel (if the work queue is completely filled).  In retrospect, I haven’t had the need to run more than one AsyncTask at a time (I’ve built relatively simple apps).  However, as the products I build become more sophisticated and require more parallelization, concurrent AsyncTasks will come in handy.

-> AsycnTask @ Android Developers

Identifying The Right Customers

Shift Jelly developer, Russell Ivanovic, shared some insights on the breakdown of Pocket Casts users on Android.

First of all, Android version adoption numbers.

screen-shot-2015-01-31-at-10-25-14-am

Developers should spend an overwhelming amount of their time developing and shipping products for Android 4.1 “Jellybean” – Android 4.4 “KitKat” because that’s where the users are right?  Well, it depends.

Pocket Casts specific Android adoption numbers (ie. what versions of Android are my users running).

pc_numbers

My lone Android application, NC Traffic Cams, Android adoption numbers.

Screenshot from 2015-01-31 11:33:01

So while Android 5.0 has less than 1% adoption in the overall Android eco-system, 23% of our customers already run it. This makes sense when you put a bit of thought into these numbers. People that have the money to buy apps, and are passionate about Android, have up to date phones. While some users who run Android 2.3 on their 5 year old phone might be perfectly happy, they probably weren’t ever going to buy Pocket Casts. It’s also worth noting that Pocket Casts sells in much larger volumes (and makes more revenue) than any numbers I’ve seen for an equivalent iOS app. We’ve slowly moved our minimum version from 2.3, to 4.0, to 4.1 and it hasn’t hurt sales at all.

I, 100%, agree with Rusty.

Android adoption numbers, provided by Google, are only relevant if your target customers include ALL Android users.  This is rarely the case.  I’d imagine only a very few developers and companies can afford to target all Android users as customers for their apps and services (depending on the complexity of their apps, most in this group are large public corporations or venture backed startups ie. the Facebooks, Twitters, Instagrams, and Ubers of the world who aim for ubiquity).  Android is such a huge market (1 billion devices sold in 2014) that there are bound to be smaller, profitable, niche markets available.  Enthusiast and indie developers need to spend sometime identifying these smaller, niche markets before starting on new products.

While building NC Traffic Cams, a relatively simple product, I essentially targeted Android users who were interested in North Carolina traffic imagery.  I wasn’t using the latest and greatest APIs because my priority was providing visual traffic information to as many Android users as possible.  Early on, I used libraries (ActionSherlock, Google Play Services, etc.) that made it easy for me to provide a consistent user experience across various versions of Android (at that time, starting at Android 2.1).  I eventually dropped support for Android 2.1 and 2.2 because the Google Play Services library eventually dropped support for these versions.

I, of course, did not completely follow my latest insights with regards to my current project, PremoFM.  In the beginning, I identified, at a very-high-level, business-diagram-type-of-way, the customers I am targeting, but I failed to identify the specific profile of Android users I am targeting until midway through the project.  For example, I targeted Android customers running Android 4.0 through to Android 5.0 (I at least had enough foresight to cut out Gingerbread support).  Then I began writing code and realized how many specific APIs weren’t available on older versions of Android.  I was definitely on the road to a bad user experience and high support costs, given my real life constraints (one person, doing this in my free time).  I took a step back and asked a few questions, some of them being:

  • How much effort or time am I willing to sacrifice to provide support to users with older devices (fixing bugs specific to these version, etc.)?
  • What versions of Android will allow me to provide the best user experience possible?
  • Are there enough paying customers using older versions of Android to make supporting these versions worthwhile?

In the end, it’s all about establishing your priorities (and more importantly, de-prioritizing other things!).  The time you spend working on XYZ will mean you won’t have that time to work on ABC.

As an side, it would be really interesting to see general data on which types of users spend the most money and what versions of Android they use.  My guess is pretty consistent with Android adoption numbers provided by Russell.

-> How New Versions of Android Work @ RustysRants

 

Sending Pushes Messages using Google Cloud Messenger in PHP

Here is a quick PHP script that sends a Google Cloud Messenger push message to any number of Android devices, given you have their registration IDs (which you would have when you implement GCM in the Android app) and a Google Cloud Messenger API Key.

It’s a command line script, so you need php5-cli and php5-curl installed.  Finally give the script execute permissions.  

On my Linux based development machine, I’d do something like:

  • sudo apt-get install php5-cli php5-curl
  • chmod +x send-gcm.php
  • ./send-gcm.php

Hope this helps someone test their GCM implementation.  8-D