Navigate New York City with Google Maps for mobile

New York City Mayor Bloomberg just announced on the Official Google Blog that the city has partnered with Google to create www.nycgo.com and a brand new Official NYC Information Center, located at 810 7th Avenue. On the mobile team, we're particularly excited by how New York City is helping visitors navigate the city by integrating Google Maps for mobile into the experience.

It's easy to plan a trip at home and then use your phone to navigate the city. You can browse www.nycgo.com to find a hotel, restaurants, and things to do in New York. Most web pages embed Google Maps, and using a nifty "send to phone" feature, you can send locations to your phone via text message. Later on, when you're out and about in the city, click the link in the text message to view a map, get directions, and see Street View. Don't forget that you can now get New York City subway routes on your iPhone, BlackBerry, or other smartphone!

Check out the video below to see how things work:


Next time you're out and about in New York City, use Google Maps for mobile to search for places and get directions. If you don't already have Google Maps on your phone, download it by visiting google.com/gmm on your cell phone's browser.

Avoiding memory leaks

Android applications are, at least on the T-Mobile G1, limited to 16 MB of heap. It's both a lot of memory for a phone and yet very little for what some developers want to achieve. Even if you do not plan on using all of this memory, you should use as little as possible to let other applications run without getting them killed. The more applications Android can keep in memory, the faster it will be for the user to switch between his apps. As part of my job, I ran into memory leaks issues in Android applications and they are most of the time due to the same mistake: keeping a long-lived reference to a Context.



On Android, a Context is used for many operations but mostly to load and access resources. This is why all the widgets receive a Context parameter in their constructor. In a regular Android application, you usually have two kinds of Context, Activity and Application. It's usually the first one that the developer passes to classes and methods that need a Context:




@Override
protected void onCreate(Bundle state) {
super.onCreate(state);

TextView label = new TextView(this);
label.setText("Leaks are bad");

setContentView(label);
}


This means that views have a reference to the entire activity and therefore to anything your activity is holding onto; usually the entire View hierarchy and all its resources. Therefore, if you leak the Context ("leak" meaning you keep a reference to it thus preventing the GC from collecting it), you leak a lot of memory. Leaking an entire activity can be really easy if you're not careful.



When the screen orientation changes the system will, by default, destroy the current activity and create a new one while preserving its state. In doing so, Android will reload the application's UI from the resources. Now imagine you wrote an application with a large bitmap that you don't want to load on every rotation. The easiest way to keep it around and not having to reload it on every rotation is to keep in a static field:




private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
super.onCreate(state);

TextView label = new TextView(this);
label.setText("Leaks are bad");

if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);

setContentView(label);
}


This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)



This example is one of the simplest cases of leaking the Context and you can see how we worked around it in the Home screen's source code (look for the unbindDrawables() method) by setting the stored drawables' callbacks to null when the activity is destroyed. Interestingly enough, there are cases where you can create a chain of leaked contexts, and they are bad. They make you run out of memory rather quickly.



There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid escaping the context outside of its own scope. The example above showed the case of a static reference but inner classes and their implicit reference to the outer class can be equally dangerous. The second solution is to use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. If you plan on keeping long-lived objects that need a context, remember the application object. You can obtain it easily by calling Context.getApplicationContext() or Activity.getApplication().



In summary, to avoid context-related memory leaks, remember the following:



  • Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)

  • Try using the context-application instead of a context-activity

  • Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance

  • A garbage collector is not an insurance against memory leaks



Note: This article was originally posted on my personal blog.

Recap of Macworld 2009

Last week the Google Mobile team joined Googlers from the Geo, YouTube and Mac teams at the 2009 Macworld Expo at the Moscone Center in San Francisco. This year, the Google booth featured fun giveaways, a YouTube upload booth, an Earth Surfer, and an iPhone charging station. Check out some pictures from the Google booth:



Along with demoing mobile products for the iPhone and iPod touch, we also shared the secret behind the hidden options in the Google Mobile App. If you didn't get a chance to stop by the booth at Macworld and haven't discovered the secret, you can get some hints from bloggers, tech sites, or vloggers.

We hope you enjoy the Bells and Whistles! Also, we wanted to give a big thanks to our users and the Mac Observer which gave the Google Mobile App the 2009 Editor's Choice Award!

Why is my list black? An Android optimization

ListView is one of Android's most widely used widgets. It is rather easy to use, very flexible and incredibly powerful. ListView can also be difficult to understand at times.


One of the most common issues with ListView happens when you try to use a custom background. By default, like many Android widgets, ListView has a transparent background which means yo can see through the default window's background, a very dark gray (#FF191919 with the current dark theme.) Additionally, ListView enables the fading edges by default, as you can see at the top of the following screenshot; the first text item gradually fades to black. This technique is used throughout the system to indicate that the container can be scrolled.


Android's default ListView

The fade effect is implemented using a combination of Canvas.saveLayerAlpha() and the Porter-Duff Destination Out blending mode. This technique is similar to the one explained in Filthy Rich Clients and various presentations. Unfortunately, things start to get ugly when you try to use a custom background on the ListView or when you change the window's background. The following two screenshots show what happens in an application when you change the window's background. The left image shows what the list looks like by default and the right image shows what the list looks like during a scroll initiated with a touch gesture:


Dark fadeDark list

This rendering issue is caused by an optimization of the Android framework enabled by default on all instances of ListView (for some reason, I forgot to enable it by default on GridView.) I mentioned earlier that the fade effect is implemented using a Porter-Duff blending mode. This implementation works really well but is unfortunately very costly and can bring down drawing performance by quite a bit as it requires to capture a portion of the rendering in an offscreen bitmap and then requires extra blending (which implies readbacks from memory.)


Since ListView is most of the time displayed on a solid background, there is no reason to go down that expensive route. That's why we introduced an optimization called the "cache color hint." The cache color hint is an RGB color set by default to the window's background color, that is #191919 in Android's dark theme. When this hint is set, ListView (actually, its base class View) knows it will draw on a solid background and therefore replaces th expensive saveLayerAlpha()/Porter-Duff rendering with a simple gradient. This gradient goes from fully transparent to the cache color hint value and this is exactly what you see on the image above, with the dark gradient at the bottom of the list. However, this still does not explain why the entire list turns black during a scroll.


As I said before, ListView has a transparent/translucent background by default, and so all default Android widgets. This implies that when ListView redraws its children, it has to blend the children with the window's background. Once again, this requires costly readbacks from memory that are particularly painful during a scroll or a fling when drawing happens dozen of times per second. To improve drawing performance during scrolling operations, the Android framework reuses the cache color hint. When this hint is set, the framework copies each child of the list in a Bitmap filled with the hint value (this assumes that another optimization, called scrolling cache, is not turned off.) ListView then blits these bitmaps directly on screen and because these bitmaps are known to be opaque, no blending is required. And since the default cache color hint is #191919, you get a dark background behind each item during a scroll.


To fix this issue, all you have to do is either disable the cache color hint optimization, if you use a non-solid color background, or set the hint to the appropriate solid color value. This can be dome from code or preferably from XML, by using the android:cacheColorHint attribute. To disable the optimization, simply use the transparent color #00000000. The following screenshot shows a list with android:cacheColorHint="#00000000" set in the XML layout file:


Fade on a custom background

As you can see, the fade works perfectly against the custom wooden background. I find the cache color hint feature interesting because it shows how optimizations can make developers' life more difficult in some situations. In this particular case, however, the benefit of the default behavior outweighs the added complexity for the developer.


Note: this article was originally posted on my personal blog.

Getting directions to businesses now easier on Google Maps for mobile

One of my favorite things about Google Maps for mobile is finding businesses on the go. Today we launched a server-side change that makes it easier to get directions to businesses. You can now get directions to restaurants, stores, and other points of interest by entering their names in the start or end point. Consider this example:


Until today, if you tried to get directions and entered a business name, "starbucks" for example, as the end or start point, Google Maps for mobile did not understand that you meant Starbucks the business. Rather, Google Maps for mobile would search for an address with "starbucks" in it.

Google Maps for mobile now recognizes when your end or start point is a business and not an address. Try setting "starbucks" or "mcdonald's" as an end point when getting directions, and you'll quickly see what we mean.

If you've already got Google Maps on your BlackBerry, Nokia S60, T-Mobile G1, iPhone, or other smartphone, then you don't have to download anything to start using this new feature. This is because today's launch was made on our servers, not the Google Maps for mobile client.

But if you don't already have Google Maps for mobile, visit google.com/gmm on your phone to download it today.

Posted by Constantinos Michael, Software Engineer

Can I use this Intent?

Android offers a very powerful and yet easy to use tool called intents. An intent can be use to turn applications into high-level libraries and make code re-use something even better than before. The Android Home screen and AnyCut use intents extensively to create shortcuts for instance. While it is nice to be able to make use of a loosely coupled API, there is no guarantee that the intent you send will be received by another application. This happens in particular with 3rd party apps, like Panoramio and its RADAR intent.


While working on a new application, I came up with a very simple way to find out whether the system contains any application capable of responding to the intent you want to use. I implemented this technique in my application to gray out the menu item that the user would normally click to trigger the intent. The code is pretty simple and easy to follow:



/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}

Here is how I use it:



@Override
public boolean onPrepareOptionsMenu(Menu menu) {
final boolean scanAvailable = isIntentAvailable(this,
"com.google.zxing.client.android.SCAN");

MenuItem item;
item = menu.findItem(R.id.menu_item_add);
item.setEnabled(scanAvailable);

return super.onPrepareOptionsMenu(menu);
}

In this example, the menu is grayed out if the Barcode Scanner application is not installed. Another, simpler, way to do this is to catch the ActivityNotFoundException when calling startActivity() but it only lets you react to the problem, you cannot predict it and update the UI accordingly to prevent the user from doing something that won't work. The technique described here can also be used at startup time to ask the user whether he'd like to install the missing package, you can then simply redirect him to the Android Market by using the appropriate URI.


Note: this article was originally posted on my personal blog.

Ring in the New Year with Bells and Whistles

Happy New Year!

One evening in Zurich, after a late night cake run, we decided that our iPhone app, Google Mobile App with Voice Search, could do with a little bling. The result of this is a few extra options hidden below the preferences on the Settings tab. Trust us, they're down there... it just might take some perseverance to get to the bottom of things. Just keep trying!

Does blue not go with your new belt? Use the Theme Color to style the app to work with any outfit or match the holiday season.

Tired of the standard beeps and boops during speech recognition? You can try out some alternate sounds made by our teammates in their sugar-induced stupor.

There are a couple of other options hidden in there, too. We had (a little too much) fun implementing these features and hope you enjoy them!

And don't worry. If you don't find them, just come see us at Macworld and we'll show you.