"

Not only should your apps stop relying on the hardware Menu button, but you should stop thinking about your activities using a “menu button” at all. Your activities should provide buttons for important user actions directly in the action bar (or elsewhere on screen). Those that can’t fit in the action bar end up in the action overflow[…]

This might seem like splitting hairs over terminology, but the name action overflow promotes a different way of thinking. Instead of thinking about a menu that serves as a catch-all for various user options, you should think more about which user options you want to display on the screen as actions. Those that don’t need to be on the screen can overflow off the screen.

"
"

The share of adults in the United States who own tablet computers nearly doubled from 10% to 19% between mid-December and early January and the same surge in growth also applied to e-book readers, which also jumped from 10% to 19% over the same time period.

The number of Americans owning at least one of these digital reading devices jumped from 18% in December to 29% in January.

"
OutOfMemoryError, ImageView and TransitionDrawable - A Sneaky Memory Leak with Improper use of TransitionDrawables with ImageViews

We like to use animations and gradual transitions instead of abrupt view changes, so a lot of our ImageViews use TransitionDrawable instead of directly displaying an image. 

Definition of TransitionDrawables from the Android documentation:

An extension of LayerDrawables that is intended to cross-fade between the first and second layer.

Today we ran into a scenario that was resulting in memory leaks and ultimately an OutOfMemoryError after a number of transitions. We tracked down the issue, and I wanted to share it here in case anyone else is running into similar problems.

The scenario is this - we’re displaying a series of images that we download over the internet. When we create the activity we display a placeholder image and then, when the first image is finished downloading, we display a TransitionDrawable consisting of 1) the previous image and 2) the newly downloaded image. Then, as we download each successive image, we display a new TransitionDrawable containing 1) whatever the ImageView was displaying before and 2) the newly downloaded image.

The code looks like this:


arrayDrawable[0] = imageView.getDrawable();
arrayDrawable[1] = newBitmapDrawable;

TransitionDrawable transitionDrawable
        = new TransitionDrawable(arrayDrawable);
transitionDrawable.setCrossFadeEnabled(true);
  
imageView.setImageDrawable(transitionDrawable);
  
transitionDrawable.startTransition(500);

(do you see the problem?)

The code resulted in an ever-growing native memory heap, a good indication of a memory leak involving bitmaps. (the memory used for Bitmaps is allocated using native code, and is not garbage collected, but is released by the bitmap’s destructor instead)

First thing we tried was to call recycle() manually. This resulted in the android.graphics.Canvas.throwIfRecycled RuntimeException, indicating that the bitmap was still being used, and something is trying to draw it on the screen. This was a hint - whatever was holding onto it and preventing us from recycling() it, was also responsible for the memory leak evident when observing the native heap.

After a bit of digging around, the problem became obvious: Because we always use the old Drawable from the ImageView as the start state for the new TransitionDrawable, and then we set it right back as the new ImageView drawable, each bitmap was being retained in an infinitely growing stack of nested TransitionDrawables.

Let me illustrate this with a bit of pseudocode:

First transition, from placeholder Bitmap to newly downloaded Bitmap:

TransitionDrawable
{
 layers:[
   0:bitmap, 
   1:bitmap
 ]
}

Second transition:

TransitionDrawable
{
 layers:[
   0:TransitionDrawable
       {
         layers:[
           0:bitmap, 
           1:bitmap
          ]
        }, 
   1:bitmap
 ]
}

Third transition:


TransitionDrawable
{
 layers:[
   0:TransitionDrawable
   {
     layers:[
       0:TransitionDrawable
       {
         layers:[
           0:bitmap, 
           1:bitmap
          ]
        }, 
       1:bitmap
     ]  
   }
   1:bitmap
 ]
}

And so on…

Once we pinpointed the problem, the fix was easy. There are two options:

1)  after each transition, reset the imageView drawable to the new BitmapDrawable - the layer 1, or the final state of the TransitionDrawable. Since the TransitionDrawable does not have a completion listener, you wil need to schedule the resetting yourself.


//use your scheduling mechanism of choice
//Ticker, Handler.postDelayed
imageView.setImageDrawable(drawable);

This has the benefit of making sure that only 1 bitmap is retained post transition.


2) On each successive transition, check to see if the ImageView already contains a TransitionDrawable, and then grab the currently visible layer (layer 1 / final layer) to use as the starting state for the new transition.

Drawable oldDrawable = imageView.getDrawable();
BitmapDrawable oldBitmapDrawable = null;
if (oldDrawable instanceof TransitionDrawable)
{
  TransitionDrawable oldTransitionDrawable = (TransitionDrawable)oldDrawable;
  oldBitmapDrawable =(BitmapDrawable)  (oldTransitionDrawable).findDrawableByLayerId(1);
}
else if (oldDrawable instanceof BitmapDrawable)
{
  oldBitmapDrawable = (BitmapDrawable) oldDrawable;
}

Either of these approaches will prevent the infinitely growing stack of TransitionDrawables, and allow the Bitmaps to be recycled when no longer needed.

To anyone unfamiliar with Android, this just looks like a bunch of gibberish.

To anyone unfamiliar with Android, this just looks like a bunch of gibberish.

Android Math Just Doesn’t Compute (or The Anatomy of a Straw Man)

A friend recently sent me a link to this post titled “Android math just doesn’t compute”

The post starts by asking “What if I told you that you shouldn’t support Android?” and then goes on to list several supporting points. The points are extremely light on substance, but I’ll take the bait anyway and address them one by one.

…that there isn’t a single Android device that maintains double digit marketshare?
This is probably true, yet completely irrelevant. This is analogous to the fact that there isn’t a single Windows device that maintains double-digit marketshare. By this logic, we should not develop web or desktop apps for Windows either. The device marketshare is irrelevant. The platform marketshare is what matters.

…that less than 20% of your customers will use an Android device?
It’s been a few months since I lifted my head from coding and looked at the market numbers, but last time I did so the consensus was that Android is ahead of iPhone in number of phone users, and growing at a faster rate to boot. When tablets are added to the mix, iPhone is ahead, for now.

…that over half of all Android devices are out of date before they hit the market?
For some definition of “out of date”, this is probably true. He is probably referring to devices sold with a version of Android that is not the latest version. (e.g. 2.2 instead of 2.3) Again, this is true, but not very relevant. It is mostly frustrating to me as a consumer, and fairly irrelevant to me as a developer. One app can easily be made to run on all versions of Android. The only time this affects us is when we need to develop an app that requires a feature present only in the latest version of the OS.

…that 85% of all Android devices can’t run the most recent version of the platform?
I take it this is referring to Android 4.0. This sounds true. However, as mentioned above, this is not relevant, except for apps that need specific features - such as the new face-detection service - which are present only in the latest version.

…that you should not use the same HTML5 mobile app for both iOS and Android?
They don’t state why, so not sure how to address the point, other than to say we’ve done so, with great results.

…that supporting even a small subset of Android devices can triple the time it takes to get your product to market?
I would strongly disagree with this. Every app we build supports >97% of current devices, and the effort is comparable to building the equivalent apps on iPhone, with some added testing and tweaking required for the various device profiles. Nowhere even close to triple.

The post starts off by advising against supporting Android, and then expands on that by saying that you should not even try to develop cross-platform HTML5 mobile apps that run on Android. Android is the dominant smartphone platform, both surpassing in number of users, and growing faster than, all other platforms. Advising against supporting it, in my opinion, is what does not compute.

A sneaky reason for the “No mountable file systems” error when mounting a .dmg file

This is usually caused by corrupt .dmg files. However, if you keep seeing this error with every .dmg file you try, check whether your Migration Assistant is running, then shut it down.


Rock, Paper, Scissors, Lizard, Spock

Rock, Paper, Scissors, Lizard, Spock

The problem with emulating Y Combinator’s terms

There seems to be a trend among early stage investment companies towards offering terms similar to what Y Combinator does - let’s ignore the fact that these terms tend to vary, and just call it 20K for 10% - a 200K valuation.

There is a problem with this line of reasoning, and this problem is: they are not Y Combinator.

The reality is that Y Combinator is not a cash-for-stock deal. There are many other intangibles that come with the deal:

1) The program - 3 months of intense mentoring

2) The connections - not the least of which are the ones brought in for Demo Day

3) The automatic 100K “bridge loan” convertible note given to each alum

4) The halo effect - being a YC alum opens doors

5) The alumni network - the ability to reach out to any one of the other smart, capable, successful alumni

To illustrate the last point (and give me a chance to brag): I recently had a chance to chat with Paul Bucheit about this topis, and he stressed how much the YC alumni stick together - use each other’s services, refer business to one another, advise and collaborate, and even help each other with recruiting - which is normally a very competitive thing. What is the value of such a tightly knit network of superstars backing you?

Bottom line is, 20K + the YC intangibles for 10% is not the same thing as 20K for 10%. You can’t ignore the most important part of the deal. 

So, what should a straight cash-for-stock deal look like? In my opinion, a simple, no strings convertible note, perhaps with a bit of a discount to reward the investor for their early bet on the company. This simplifies things for the company, and lets them delay having to worry about valuations until they are ready to do the due diligence required to raise serious cash.

Audience Extension - a new trend among Ad Networks

We were recently approached by a well-known ad network, with an interesting proposition.

They offered to pay us for providing an anonymized global user identifier - a hashed email address or phone number for each user. Anonymized, one-way hashed, so they don’t get the actual email or number, but using their hash so they can cross reference with their other publishers, for global user tracking across properties, apps or sites.

To sweeten this deal, they also offer to provide us with free extended user data, based on the submitted GUID. (demo, CPG, lifestyle, 100+ segments) and use behavioral targeting, across their entire network, to optimize our ad-targeting.

And here is the very interesting bit - based on the GUID, they can now give us the ability to sell inventory against our audience across their entire network - even when the user is on another publisher’s property.

I hear there are a couple of other networks doing this same thing - anyone else seeing this trend?

Troubleshooting the “conversion to dalvik format failed with error 1” Android / Eclipse errors

I spent a bit of time troubleshooting this obscure-sounding error today, so I thought I’d share the solution I found. 

Turns out, this error is frequently caused by having multiple instances of the same class in your buildpath - e.g. importing 2 jars that both include the same third party library. Double-check the last thing you imported before the error occurred.

1 of 8