Saturday, July 24, 2010

[Gd] Adjustment to Market Legals

| More

Android Developers Blog: Adjustment to Market Legals

Please note that we have updated the Android Market Developer Distribution Agreement (DDA). This is in preparation for some work we’re doing on introducing new payment options, which we think developers will like.

In the spirit of transparency, we wanted to highlight the changes:

  • In Section 13.1, “authorized carriers” have been added as an indemnified party.

  • Section 13.2 is new in its entirety, covering indemnity for payment processors for claims related to tax accrual.

These new terms apply immediately to anyone joining Android Market as a new publisher. Existing publishers have been notified of this change via email; they have up to 30 days to sign into the Android Market developer console to accept the new terms.


Friday, July 23, 2010

[Gd] Introducing the Mapper API

| More

Google App Engine Blog: Introducing the Mapper API

At Google I/O we announced the Mapper API. Built completely on top of public App Engine APIs today, this API is only the first component of App Engine’s MapReduce toolkit, but can be extremely useful on its own.

The Mapper API can already be of use to many developers who would otherwise need to build their own tool for doing large scale data manipulation. In addition to taking care of the distribution of these jobs over task queues, it provides the ability to store state, batch datastore writes via mutation pools, and ships with an easy to use administrative interface for job management, all optimized for the constraints of App Engine’s dynamic serving environment. Some examples of the types of operations that work with minimal configuration with this tool:

  • Report Generation
  • Large scale migration of entity properties
  • Datastore cleanup
  • Computing statistics and metrics
For an introduction to the Mapper API, watch Mike Aizatsky’s video from Google I/O, where he demonstrates building a source code indexer. The slides can be downloaded here, and the video is below:

The App Engine team has also written a few great articles on how to use the Mapper API.
  • For Python developers, take a look at the Python Mapper API post on Nick Johnson’s blog.
  • For Java developers, Ikai Lan has written a great post about the Java Mapper API, which takes some design cues from Hadoop’s API and includes several examples of common operations such as large scale modification of properties or batch delete.

When you’re ready to jump in and start using the tool, head over to the project homepage on Google Code. You’ll want to check out the “Getting Started” page for the language you’re using:

Happy Mapping!

- Fred, Mike, Ikai, Nick + the App Engine team


[Gd] Release Early, Release Often

| More

Chromium Blog: Release Early, Release Often

Over the next few months, we are going to be rolling out a new release process to accelerate the pace at which Google Chrome stable releases become available. Running under ideal conditions, we will be looking to release a new stable version about once every six weeks, roughly twice as often as we do today.

So why the change? We have three fundamental goals in reducing the cycle time:
  • Shorten the release cycle and still get great features in front of users when they are ready
  • Make the schedule more predictable and easier to scope
  • Reduce the pressure on engineering to “make” a release
The first goal is fairly straightforward, given our pace of development. We have new features coming out all the time and do not want users to have to wait months before they can use them. While pace is important to us, we are all committed to maintaining high quality releases — if a feature is not ready, it will not ship in a stable release.

The second goal is about implementing good project management practice. Predictable fixed duration development periods allow us to determine how much work we can do in a fixed amount of time, and makes schedule communication simple. We basically wanted to operate more like trains leaving Grand Central Station (regularly scheduled and always on time), and less like taxis leaving the Bronx (ad hoc and unpredictable).

The third goal is about taking the pressure off software engineers to finish features in a single release cycle. Under the old model, when we faced a deadline with an incomplete feature, we had three options, all undesirable: (1) Engineers had to rush or work overtime to complete the feature by the deadline, (2) We delayed the release to complete that feature (which affected other un-related features), or (3) The feature was disabled and had to wait approximately 3 months for the next release. With the new schedule, if a given feature is not complete, it will simply ride on the the next release train when it’s ready. Since those trains come quickly and regularly (every six weeks), there is less stress.

So, get ready to see us pick up the pace and for new features to reach the stable channel more quickly. Since we are going to continue to increment our major versions with every new release (i.e. 6.0, 7.0, 8.0, 9.0) those numbers will start to move a little faster than before. Please don’t read too much into the pace of version number changes - they just mean we are moving through release cycles and we are geared up to get fresher releases into your hands!

Posted by Anthony Laforge, Program Manager

Thursday, July 22, 2010

[Gd] A New Way To Embed YouTube Videos

| More

YouTube API Blog: A New Way To Embed YouTube Videos

An enhancement to our video embed capability is now available through a new embed code style. This new style uses <iframe> and looks like this:

<iframe class="youtube-player" type="text/html" width="640" height="385" src="" frameborder="0">


If you use the new embed code style, your viewers will be able to view your embedded video in one of our Flash or HTML5 players, depending on their viewing environment and preferences. Environments that support the HTML5 video player are listed here on our HTML5 settings page. In instances where HTML5 isn't supported (e.g. our HTML5 player can't play videos with ads), we use Flash.

An additional benefit of the new embed style is that it will eventually allow embeds to work on mobile devices, which typically use a built-in player instead of Flash or HTML5.

Before we make this new embed code the standard for our general users, we wanted to give our developer community a preview. While there may be some limitations, please try it out and
let us know how it went.

Toliver Jue, Software Engineer
recently learned some dance moves from “


Wednesday, July 21, 2010

[Gd] Dev Channel Update

| More

Google Chrome Releases: Dev Channel Update

The Dev channel has been updated to 6.0.472.0 for Windows, Chrome Frame, and Linux. A Mac release will follow soon.

  • [r52693] Fix crash with SSL client auth (Issue 49197)
  • [r52850] Option clicking a link now saves a resource directly without triggering a “Save As...” dialog (Issue 36775)

Known Issues
More details about additional changes are available in the svn log of all revisions.

You can find out about getting on the Dev channel here:

If you find new issues, please let us know by filing a bug at

Jason Kersey
Google Chrome

[Gd] Google Developer Events Worldwide

| More

Google Apps Developer Blog: Google Developer Events Worldwide

Google Developer Relations is reaching out to developers worldwide. We have Google Developer Days, DevFests, and Google Technical User Group (GTUG) events where you can learn more about Google products and APIs, and meet Google developer advocates.

Google Developer Days
are scheduled for;

Sep 28: Tokyo, Japan

Oct 29: Sao Paulo, Brazil

Nov 9: Munich, Germany

Nov 12: Moscow, Russia

Nov 16: Prague, Czech Republic

We will hold technical sessions on mobile and web technologies including Android, Chrome, Google Web Toolkit, App Engine and more. It will also be an opportunity to meet both the Google team and other developers.

Google DevFests are smaller events focused on coding and workshops. Most recently we had a week long DevFest Australia June 28 - July 2, DevFest Israel June 29th, Manila July 6th, Singapore July 9th, and Kuala Lumpur July 16th.

Earlier this year there were DevFests in Pune and Hyderabad India, and Japan.

There are several DevFests currently scheduled and more in the planning stages.

September 30 - Madrid, Spain

November 2 - Buenos Aires, Argentina

Google Technical User Groups meet in many cities across the world. Check this directory to find a GTUG near you. Most groups meet once a month and are locally organized. Google Developer Advocates are at the meetings whenever they are in town. It is a great place to meet other developers and share ideas.

Google Blogs - Another way to stay in touch with Google technical updates are Google product blogs. Google Code Blog covers all products and links to many other product blogs. Here is a fairly complete list of blogs for products and APIs.

Meet the Google Apps Marketplace Team. This page introduces you to Google Developer Advocates and other people working on Google Apps. It includes our contact information so you can reach out to us directly with questions or opportunities. We will have pages like this for each Developer Advocate team in the near future. We are here to help. Let us know what we can do for you.

Posted by Don Dodge, Google Apps Team

[Gd] Celebrating Six Months of Chromium Security Rewards

| More

Chromium Blog: Celebrating Six Months of Chromium Security Rewards

It has been approximately six months since we launched the Chromium Security Reward program. Although still early days, the program has been a clear success. We have been notified of numerous bugs, and some of the participants have made it clear that it was the reward program that motivated them to get involved with Chromium security.

We maintain a list of issued rewards on the Chromium security page. As the list indicates, a range of researchers have sent us some great bugs and the rewards are flowing! This list should also help answer questions about which sort of bugs might qualify for rewards.

Today, we are modifying the program in two ways:
  1. The maximum reward for a single bug has been increased to $3,133.7. We will most likely use this amout for SecSeverity-Critical bugs in Chromium. The increased reward reflects the fact that the sandbox makes it harder to find bugs of this severity.
  2. Whilst the base reward for less serious bugs remains at $500, the panel will consider rewarding more for high-quality bug reports. Factors indicating a high-quality bug report might include a careful test case reduction, an accurate analysis of root cause, or productive discussion towards resolution.
Thanks to everyone who contributes to Chromium security, and here’s looking forward to our first elite entrant!

Posted by Chris Evans, Google Chrome Security

Tuesday, July 20, 2010

[Gd] Code coverage goal: 80% and no less!

| More

Google Testing Blog: Code coverage goal: 80% and no less!

by Alberto Savoia

I first posted this article a few years ago on the Artima Developer website; but the question of what's adequate code coverage keeps coming up, so I thought it was time for a repost of Testivus wisdom on the subject.

Testivus on Test Coverage

Early one morning, a young programmer asked the great master:

“I am ready to write some unit tests. What code coverage should I aim for?”

The great master replied:

“Don’t worry about coverage, just write some good tests.”

The young programmer smiled, bowed, and left.

Later that day, a second programmer asked the same question.

The great master pointed at a pot of boiling water and said:

“How many grains of rice should put in that pot?”

The programmer, looking puzzled, replied:

“How can I possibly tell you? It depends on how many people you need to feed, how hungry they are, what other food you are serving, how much rice you have available, and so on.”

Exactly,” said the great master.

The second programmer smiled, bowed, and left.

Toward the end of the day, a third programmer came and asked the same question about code coverage.

“Eighty percent and no less!” Replied the master in a stern voice, pounding his fist on the table.

The third programmer smiled, bowed, and left.

After this last reply, a young apprentice approached the great master:

“Great master, today I overheard you answer the same question about code coverage with three different answers. Why?”

The great master stood up from his chair:

“Come get some fresh tea with me and let’s talk about it.”

After they filled their cups with smoking hot green tea, the great master began:

“The first programmer is new and just getting started with testing. Right now he has a lot of code and no tests. He has a long way to go; focusing on code coverage at this time would be depressing and quite useless. He’s better off just getting used to writing and running some tests. He can worry about coverage later.

The second programmer, on the other hand, is quite experience both at programming and testing. When I replied by asking her how many grains of rice I should put in a pot, I helped her realize that the amount of testing necessary depends on a number of factors, and she knows those factors better than I do – it’s her code after all. There is no single, simple, answer, and she’s smart enough to handle the truth and work with that.”

“I see,” said the young apprentice, “but if there is no single simple answer, then why did you tell the third programmer ‘Eighty percent and no less’?”

The great master laughed so hard and loud that his belly, evidence that he drank more than just green tea, flopped up and down.

“The third programmer wants only simple answers – even when there are no simple answers … and then does not follow them anyway.”

The young apprentice and the grizzled great master finished drinking their tea in contemplative silence.


[Gd] Discover v201003: Migrating your Account Structure Reports

| More

AdWords API Blog: Discover v201003: Migrating your Account Structure Reports

Account structure report is one of the popular report formats in v13 of the AdWords API. This report allows you to download all the account attributes in a single report without performance details. A common use of account structure reports is to sync a local database against the AdWords server without using more expensive get() API calls.

Despite being a useful report format, account structure report is a specialized report format available only through the AdWords API. We designed the new API such that standard report formats can support your needs rather than creating additional specialized report types. Therefore, the new AdWords API doesn’t have a separate account structure report type, but you can use the existing report types supported by ReportDefinitionService to download the same data. This blog post discusses how this can be done, so that you can migrate your existing account structure reports to the v201003 version of the AdWords API. This blog post assumes you are familiar with the usage of ReportDefinitionService, if not, we suggest that you read our introductory blog post first.

Retrieving campaign structure information

You can download all campaigns in an account in the v201003 version of the AdWords API using CAMPAIGN_PERFORMANCE_REPORT. The xml request for adding the report definition is shown below:
<mutate xmlns="">
      <reportName>Campaign performance report #1277301277814.61</reportName>
Retrieving the ad group structure information

You can download all adgroups in an account in the v201003 version of the AdWords API using ADGROUP_PERFORMANCE_REPORT. The xml request for adding the report definition is shown below:
<mutate xmlns="">
      <reportName>AdGroup performance report #1277302184111.48</reportName>

If required, you can restrict the report to contain ad groups from only a particular campaign by setting a Predicate. A code example showing the usage of Predicate can be found here.

Retrieving the ad structure information

You can download all ads in an account in the v201003 version of the AdWords API using AD_PERFORMANCE_REPORT. The xml request for adding the report definition is shown below:
<mutate xmlns="">
      <reportName>Ad performance report #1277303749892.73</reportName>
If required, you can restrict the report to contain ads from only a particular ad group by setting a Predicate.

Retrieving keyword and placement structure information

You can download all keywords in an account in the v201003 version of the AdWords API using KEYWORD_PERFORMANCE_REPORT. Similarly, all placements can be downloaded using MANAGED_PLACEMENTS_PERFORMANCE_REPORT. The xml requests for adding the keyword performance report definition are shown below:
<mutate xmlns="">
      <reportName>Keyword performance report #1277353190509.28</reportName>

The xml for placement report is identical to the one shown above, except for the reportType field. Note that have to use the KeywordText field to get the placement url while running MANAGED_PLACEMENTS_PERFORMANCE_REPORT.

Putting it all together

Here’s a code example that puts everything together to download your account structure using reports. The code snippet for parsing xmls and copying streams has been excluded for brevity.

Click here to expand
public class Program {
  public static void main(String[] args) throws FileNotFoundException, IOException,
      ServiceException {
    AdWordsUser user = new AdWordsUser(); 
    // Download all campaigns.
    GetReport(user, ReportDefinitionReportType.CAMPAIGN_PERFORMANCE_REPORT,
        new String[] { "Id", "Name", "Status", "Amount" }, "AllCampaigns.xml");

    // Download all adgroups.
    GetReport(user, ReportDefinitionReportType.ADGROUP_PERFORMANCE_REPORT,
        new String[] { "Id", "Name", "Status", "CampaignId"}, "AllAdGroups.xml");

    // Download all ads.
    GetReport(user, ReportDefinitionReportType.AD_PERFORMANCE_REPORT,
        new String[] { "Id", "Headline", "Description1", "Description2", "DisplayUrl",
        "Url", "Status", "AdGroupId" }, "AllAds.xml");

    // Download all keywords.
    GetReport(user, ReportDefinitionReportType.KEYWORDS_PERFORMANCE_REPORT,
        new String[] { "AdGroupId", "Id", "KeywordText", "KeywordMatchType", "IsNegative"},

    // Download all placements.
    GetReport(user, ReportDefinitionReportType.MANAGED_PLACEMENTS_PERFORMANCE_REPORT,
        new String[] { "AdGroupId", "Id", "KeywordText", "KeywordMatchType", "IsNegative"},
  public static void GetReport(AdWordsUser user, ReportDefinitionReportType reportType,
      String[] fields, String fileName) throws FileNotFoundException, IOException,
      ServiceException {
    long reportDefinitionId = GenerateReportDefinition(user, reportType, fields);
    DownloadReport(user, reportDefinitionId,
  public static void DownloadReport(AdWordsUser user, long reportDefinitionId,
     String fileName) throws FileNotFoundException, IOException {
     String url = "" +

    HttpURLConnection urlConn = (HttpURLConnection) new URL(url).openConnection();
    urlConn.setRequestProperty("Authorization", "GoogleLogin auth=" +

    if (user.getClientCustomerId() != null) {
      urlConn.setRequestProperty("clientCustomerId", user.getClientCustomerId());
    } else if (user.getClientEmail() != null) {
      urlConn.setRequestProperty("clientEmail", user.getClientEmail());
    } else {
      urlConn.setRequestProperty("clientEmail", user.getEmail());

    writeStreamToStream(urlConn.getInputStream(), new FileOutputStream(new File(fileName)));

  public static long GenerateReportDefinition(AdWordsUser user,
      ReportDefinitionReportType reportType, String[] fields) throws ApiException,
      RemoteException, ServiceException {
    // Get the ReportDefinitionService.
    ReportDefinitionServiceInterface reportDefinitionService = user

    // Create selector.
    Selector selector = new Selector();

    // Create report definition.
    ReportDefinition reportDefinition = new ReportDefinition();
    reportDefinition.setReportName("Structure report demo - #" +

    // Create operations.
    ReportDefinitionOperation operation = new ReportDefinitionOperation();

    ReportDefinitionOperation[] operations = new ReportDefinitionOperation[] { operation };

    // Add report definition.
    ReportDefinition[] result = reportDefinitionService.mutate(operations);
    return result[0].getId();

We've included support for ReportDefinitionService in all of our client libraries to help get you started, so please try it out and share your feedback with us on the forum or the projects' issue trackers.

-- Anash P. Oommen, AdWords API Team

Monday, July 19, 2010

[Gd] New Google Buzz API features, including a hose of fire

| More

Google Code Blog: New Google Buzz API features, including a hose of fire

Since we introduced the Google Buzz API at Google I/O, we’ve been working hard to make it better, broader, and more useful. Today we're introducing several new features that are the direct result of your feedback.

We're launching the Google Buzz firehose — our top developer feature request. With the firehose, all public activities are available as they are published with a single subscription, thanks to syndication via PubSubHubbub.

We’ve had some fun coming up with cool things to do with the firehose. For example, Bob Aman coded up Buzz Mood, an App Engine app inspired by Twistori. By scanning for posts that contain certain keywords, Bob’s able to give us a sense for the mood across all of Google Buzz in real time. Definitely take a look at the the source to get ideas for your own apps!

For more inspiration, also check out our firehose launch partners. Integrating with the firehose today are Collecta, Gnip, OneRiot, Postrank Analytics, and Superfeedr’s Track.

We’re making these new API features available starting today:
  • Comments by the user - This feed consists of the activities the user has commented on.
  • Likes by the user - The activities the user has liked are in this feed.
  • Shared counts - This will return the number of times a specified URL has been shared across Google Buzz.
All of these features are documented in much more detail on the Google Buzz API documentation site and can be discussed on the Developer Forum. We will continue to innovate and iterate the Buzz API and encourage you to check out the new features and let us know what you think.

By John Panzer, Google Buzz Team

[Gd] Multithreading For Performance

| More

Android Developers Blog: Multithreading For Performance

[This post is by Gilles Dubunne, an engineer in the Android group who loves to get multitasked. — Tim Bray]

A good practice in creating responsive applications is to make sure your main UI thread does the minimum amount of work. Any potentially long task that may hang your application should be handled in a different thread. Typical examples of such tasks are network operations, which involve unpredictable delays. Users will tolerate some pauses, especially if you provide feedback that something is in progress, but a frozen application gives them no clue.

In this article, we will create a simple image downloader that illustrates this pattern. We will populate a ListView with thumbnail images downloaded from the internet. Creating an asynchronous task that downloads in the background will keep our application fast.

An Image downloader

Downloading an image from the web is fairly simple, using the HTTP-related classes provided by the framework. Here is a possible implementation:

static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);

try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
return null;

final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
} catch (Exception e) {
// Could provide a more explicit error message for IOException or IllegalStateException
Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());
} finally {
if (client != null) {
return null;

A client and an HTTP request are created. If the request succeeds, the response entity stream containing the image is decoded to create the resulting Bitmap. Your applications' manifest must ask for the INTERNET to make this possible.

Note: a bug in the previous versions of BitmapFactory.decodeStream may prevent this code from working over a slow connection. Decode a new FlushedInputStream(inputStream) instead to fix the problem. Here is the implementation of this helper class:

static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {

public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n-totalBytesSkipped);
if (bytesSkipped == 0L) break;
totalBytesSkipped += bytesSkipped;
return totalBytesSkipped;

This buffer manipulation reads the entire input stream in a byte array that can then be decoded as a Bitmap.

If you were to directly use this method in your ListAdapter's getView method, the resulting scrolling would be unpleasantly jaggy. Each display of a new view has to wait for an image download, which prevents smooth scrolling.

Indeed, this is such a bad idea that the AndroidHttpClient does not allow itself to be started from the main thread. The above code will display "This thread forbids HTTP requests" error messages instead. Use the DefaultHttpClient instead if you really want to shoot yourself in the foot.

Introducing asynchronous tasks

The AsyncTask class provides one of the simplest ways to fire off a new task from the UI thread. Let's create an ImageDownloader class which will be in charge of creating these tasks. It will provide a download method which will assign an image downloaded from its URL to an ImageView:

public class ImageDownloader {

public void download(String url, ImageView imageView) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);

/* class BitmapDownloaderTask, see below */

The BitmapDownloaderTask is the AsyncTask which will actually download the image. It is started using execute, which returns immediately hence making this method really fast which is the whole purpose since it will be called from the UI thread. Here is the implementation of this class:

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;

public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);

// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);

// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;

if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {

The doInBackground method is the one which is actually run in its own process by the task. It simply uses the downloadBitmap method we implemented at the beginning of this article.

onPostExecute is run in the calling UI thread when the task is finished. It takes the resulting Bitmap as a parameter, which is simply associated with the imageView that was provided to download and was stored in the BitmapDownloaderTask. Note that this ImageView is stored as a WeakReference, so that a download in progress does not prevent a killed activity's ImageView from being garbage collected. This explains why we have to check that both the weak reference and the imageView are not null (i.e. were not collected) before using them in onPostExecute.

This simplified example illustrates the use on an AsyncTask, and if you try it, you'll see that these few lines of code actually dramatically improved the performance of the ListView which now scrolls smoothly. Read Painless threading for more details on AsyncTasks.

However, a ListView-specific behavior reveals a problem with our current implementation. Indeed, for memory efficiency reasons, ListView recycles the views that are displayed when the user scrolls. If one flings the list, a given ImageView object will be used many times. Each time it is displayed the ImageView correctly triggers an image download task, which will eventually change its image. So where is the problem? As with most parallel applications, the key issue is in the ordering. In our case, there's no guarantee that the download tasks will finish in the order in which they were started. The result is that the image finally displayed in the list may come from a previous item, which simply happened to have taken longer to download. This is not an issue if the images you download are bound once and for all to given ImageViews, but let's fix it for the common case where they are used in a list.

Handling concurrency

To solve this issue, we should remember the order of the downloads, so that the last started one is the one that will effectively be displayed. It is indeed sufficient for each ImageView to remember its last download. We will add this extra information in the ImageView using a dedicated Drawable subclass, which will be temporarily bind to the ImageView while the download is in progress. Here is the code of our DownloadedDrawable class:

static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
bitmapDownloaderTaskReference =
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);

public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();

This implementation is backed by a ColorDrawable, which will result in the ImageView displaying a black background while its download is in progress. One could use a “download in progress” image instead, which would provide feedback to the user. Once again, note the use of a WeakReference to limit object dependencies.

Let's change our code to take this new class into account. First, the download method will now create an instance of this class and associate it with the imageView:

public void download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
task.execute(url, cookie);

The cancelPotentialDownload method will stop the possible download in progress on this imageView since a new one is about to start. Note that this is not sufficient to guarantee that the newest download is always displayed, since the task may be finished, waiting in its onPostExecute method, which may still may be executed after the one of this new download.

private static boolean cancelPotentialDownload(String url, ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
} else {
// The same URL is already being downloaded.
return false;
return true;

cancelPotentialDownload uses the cancel method of the AsyncTask class to stop the download in progress. It returns true most of the time, so that the download can be started in download. The only reason we don't want this to happen is when a download is already in progress on the same URL in which case we let it continue. Note that with this implementation, if an ImageView is garbage collected, its associated download is not stopped. A RecyclerListener might be used for that.

This method uses a helper getBitmapDownloaderTask function, which is pretty straigthforward:

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
return downloadedDrawable.getBitmapDownloaderTask();
return null;

Finally, onPostExecute has to be modified so that it will bind the Bitmap only if this ImageView is still associated with this download process:

if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
if (this == bitmapDownloaderTask) {

With these modifications, our ImageDownloader class provides the basic services we expect from it. Feel free to use it or the asynchronous pattern it illustrates in your applications to ensure their responsiveness.


The source code of this article is available online on Google Code. You can switch between and compare the three different implementations that are described in this article (no asynchronous task, no bitmap to task association and the final correct version). Note that the cache size has been limited to 10 images to better demonstrate the issues.

Future work

This code was simplified to focus on its parallel aspects and many useful features are missing from our implementation. The ImageDownloader class would first clearly benefit from a cache, especially if it is used in conjuction with a ListView, which will probably display the same image many times as the user scrolls back and forth. This can easily be implemented using a Least Recently Used cache backed by a LinkedHashMap of URL to Bitmap SoftReferences. More involved cache mechanism could also rely on a local disk storage of the image. Thumbnails creation and image resizing could also be added if needed.

Download errors and time-outs are correctly handled by our implementation, which will return a null Bitmap in these case. One may want to display an error image instead.

Our HTTP request is pretty simple. One may want to add parameters or cookies to the request as required by certain web sites.

The AsyncTask class used in this article is a really convenient and easy way to defer some work from the UI thread. You may want to use the Handler class to have a finer control on what you do, such as controlling the total number of download threads which are running in parallel in this case.