Monday, April 19, 2010

Returning values from AsyncTask

While developing an application for Android, very recently, I came across an issue. I had a bunch of ListViews, each of which fetched data from a web service and displayed it. Each ListView had a different way of displaying data and different source to fetch data from.

I subclassed AsyncTask and used that to pull the data in the background and display it in the ListView. AsyncTask is easy to use and allows results of background threads to be published to the UI thread. But I ran into an issue pretty quickly. All that example I came across, implemented the AsyncTask as a nested class. For me, this meant I had to duplicate the code for every ListActivity, which, for obvious reason, I did not want to do. So, my problem definition was this:

How can one return data from an AsyncTask? More specifically, if I were to reuse AsyncTask across multiple classes, what is the better way to do it than creating a nested class?

Here is one solution:
  • Declare an interface.
public interface NetworkCallListener { 

  public void onNetworkCallComplete(Listlist); 

  public void onNetworkCallCancel();

}
  • Subclass AsyncTask
public class NetworkCallTask extends AsyncTask> {

  NetworkCallListener listener = null;

  public NetworkCallTask(NetworkCallListener listener) {
    this.listener = listener;
  }

  public void onPreExecute() {
    // do something...
  }

  @Override
  protected List doInBackground(String... args) {
    // do something
    List list = doSomethingAndGetBackAnArrayList();
    return list;
  }

  protected void onPostExecute(final List list) {
    // do something

    // Now call the listener's onNetworkCallComplete method
    // and set the value.
    listener.onNetworkCallComplete(list);
  }
}
  • Implement the interface in the ListActivity and create an instance of the AsyncTask subclass just created.
public class MyListActivity extends ListActivity implements NetworkCallListener {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // set your content view and other stuff here and finally call the async task.
    String url = "http://somwebservice.com/service";
    new NetworkCallTask(this).execute(url);
  }

  public void onNetworkCallComplete(List list) {
    // do something 

    // set the ListView's adapter. Below SomeAdapter is a subclass of ListAdapter.
    this.setListAdapter(new SomeAdapter(list));
  }
}

That is it. Now, I can just re-use the AsyncTask subclass that I wrote. I will upload a full working example to GitHub soon. Please feel free to email me or comment other ways of doing the same.

Saturday, October 31, 2009

Back after a long time.

The last few months have been very hectic and I couldn't post anything onto my blog. I worked quite a bit on Google App Engine, did some experimental projects using LAMP, messed around with Amazon EC2 and S3, researched on virtualization and tried to use CouchDB.

While mobile computing is very interesting, I feel it is also important to step out of the silo that mobile programming sometimes creates. Usability and performance are important in mobile computing but scalability is not a big factor. I really wanted to try out something far grander in scale than the iPhone and Android apps that I continuously work on.

Google App Engine projects were really fun. I churned out quite a few apps (which we use internally within our company) in a very short span of time. But I think it still has some way to go before enterprises can really leverage the platform. GAE comes with a lot of limitations. For example, you cannot have a data structure (e.g., variable) larger than 1MB in size or you'll get a memory error. While that is not an issue in most cases, I found it to be severely limiting while working on an enterprise class application. GAE has a lot of other idiosyncrasies which Aral Balkan has really nailed in his blog post on the subject.

I knew of EC2 and S3 for a while now but I never really had an excuse to try it out. The 1MB limitation was a perfect opportunity for me to try out AWS. At first, I found it very intimidating. There were tools that could easily help me get started with GAE. Getting started with EC2 on the other hand was not so easy. But after a couple of days of very intense reading, I had my first EC2 instance up and running. I am still learning the nuts and bolts of EC2, but with the release of RDS, I am positive I will enjoy the experience thoroughly.

I just started with CouchDB and haven't really done anything useful with it yet. While I am not new to a non-relational DB, I find using REST calls for db operations very interesting. I am yet to think of why I would want to use CouchDB over MySQL.

It has been a very interesting three months or so for me. My only regret has been that I could not really use these technologies/platforms earlier. But, like the idiom goes, better late than never.

Friday, June 19, 2009

Twittering with Ubiquity

Although Firefox Ubiquity was released in August last year, it was only a couple of weeks ago that I really had a chance to look at it. It is kinda difficult to explain what Ubiquity is, so I will not. Instead, I recommend you watch this video. Ubiquity can be installed from here.

Mozilla has a lot of great documentation on how to use and extend Ubiquity commands. I would recommend you start from this page.

This post deals with posting 'tweets' with Ubiquity.
  • Posting a regular text twitter: This is pretty straight-forward, you just bring up Ubiquity and type twitter and whatever message you want to post.
  • Posting a selection: Select a section of text, bring up Ubiquity and type twitter. Your selection will make up the twitter content.
But I wanted more. I wanted to tweet about the song I am currently listening to. So, I hacked together a Ubiquity command for it. Paste the following in the command editor window (ubiquity command: command-editor)

/*
Post Current Song To Twitter
*/

const TWITTER_STATUS_MAXLEN = 160;

CmdUtils.CreateCommand({
name:"tweet-song",
_getSong: function() {
var trackData = window.foxytunesGetCurrentTrackData();
return CmdUtils.makeSugg("'" + trackData.title + "' by " + trackData.artist);
},

preview:function(previewBlock) {
var song = this._getSong();
var previewTemplate = "Updates your Twitter status to:
" +
"I am listening to ${status}

" +
"Characters remaining: ${chars}";

var truncateTemplate = "
The last ${truncate} " +
"characters will be truncated.";

var previewData = {
status: song.text,
chars: TWITTER_STATUS_MAXLEN - song.text.length
};

var previewHTML = CmdUtils.renderTemplate(previewTemplate, previewData);

if (previewData.chars < 0) {
var truncateData = {
truncate: 0 - previewData.chars
};
previewHTML += CmdUtils.renderTemplate(truncateTemplate, truncateData);
}
previewBlock.innerHTML = previewHTML;
},

execute:function() {
var statusText = this._getSong();
if (statusText.chars < 0) {
displayMessage("Twitter requires a status to be entered");
return;
}
var updateUrl = "https://twitter.com/statuses/update.json";
var updateParams = {
source: "ubiquity",
status: "I am listening to " + statusText.text
};

jQuery.ajax({
type:"POST",
url: updateUrl,
data: updateParams,
dataType: "json",
error:function() {
displayMessage("Twitter error - status not updated");
},
success:function() {
displayMessage("Twitter status updated.");
}
});
}
});


You will also need to install FoxyTunes to get this command to work. That is it.

Start up a song in iTunes or any other supported player, bring up Ubiquity and type 'tweet-song' and hit enter to post a tweet that looks like 'I am listening to 'Alex Gopher With Demon Presen' by Jazz Boutique (CD Series)' (Note: You may have to login with your twitter account for this to work).

Credits: The tweet-song code is based on work by Blair McBride (twitter command) and Abimanyu Raja (get-lyrics command).

Wednesday, May 20, 2009

Debugging iPhone Crashes

Sometimes, developing iPhone applications can be very frustrating. Debugging crashes and memory issues can be very time consuming if one does not know how. Unfortunately, most books on iPhone programming either devote less than a chapter on debugging or, in most cases, totally skip it.

This post tries to de-mystify debugging crashes. When I say crashes, I mean straightforward exceptions (and not the memory access errors).

Typically, release builds are stripped of debug symbols to prevent reverse engineering (and other kinds of 'abuse') and to keep the released binary small in size. But, by doing so, you end up with only bunch of cryptic addresses in the log and absolutely no information on where the problem is (unlike in Java stack traces which pin-point the source of the exception).

On iPhone, typically, when the application is built, in the build folder (where the .app file is located), you will also find a file with a '.dSYM' extension, that contains a copy of the symbol information in the executable. This file maintains a one-to-one relationship between the source code and the compiled object code, which could make it possible to correlate an execution address with a line of code in the original source. I found that .dSYM files are generated by default. But it is a good idea to check your project settings in Xcode and ensure that 'Generate Debug Symbols' is checked.



Next, grab a copy of ratos. ratos is a ruby script that symbolicates the entire stack trace using atos (address to symbol) tool. The script is very simple to use and extremely helpful. Here is a sample output:

App Name:AtosDemo
App Path:/Users/ajeya/Documents/workspace/AtosDemo

Paste xcode stack trace to stdin, then type 'sim' or 'arm' on a line by itself.
Type 'exit' or '^D' to quit and 'app' to print the current app.

ratos>Stack: (
2483900683,
2534657595,
2483900139,
2483900202,
2478626815,
2478092680,
10809,
816608399,
10391,
816111650,
816149355,
2478330414,
2483403557,
2483403992,
827745792,
827745989,
816114848,
816160924,
10308,
10162
)
sim

-[AtosDemoViewController viewDidLoad] (in AtosDemo) (AtosDemoViewController.m:36)
-[AtosDemoAppDelegate applicationDidFinishLaunching:] (in AtosDemo) (AtosDemoAppDelegate.m:21)
main (in AtosDemo) (main.m:14)
start (in AtosDemo) + 54


Detailed instructions on how to use the script can be found on the script's homepage.

Of course, you can also choose to use 'atos' directly. But, I found ratos to be much more convenient.

Here are some useful links on debugging on iPhone and Xcode:
  1. ratos source on github
  2. Symbolification
  3. Debugging and Symbolizing Crash Dumps in Xcode
  4. Stacktraces
Reblog this post [with Zemanta]

Monday, May 18, 2009

Wolfram Alpha

The much awaited (and a bit hyped) Wolfram Alpha made its debut last week. There was a lot of talk about it being a Google killer and like everybody else I too wanted to try it out. But I was in for a surprise. Firstly, Wolfram Alpha is NOT a search engine. It is being touted as a 'computational engine' which uses an internal knowledge base and outputs computations. Confused? I was too.

My first query was for 'iPhone' which did not return anything. Next, I tried Apple and I was presented with a lot of information about Apple's stock. Information included the latest stock price, revenue, price history, performance comparisons and a lot more. I was slowly getting a gist of what it does. Next I searched for '1 USD in INR' and got some interesting results.



I, then, tried other searches like 'Hyderabad', my birth date and anything else I could think of and there were some interesting and some not so interesting nuggets of information I gathered.

So, my take on the whole thing is that Wolfram Alpha is a data aggregator. Unlike Google, they don't use the internet as the data source and rely on their own internal knowledge base. Only time will tell how effective this tool is and whether or not regular netizens will adopt this tool. I, for one, need to find a reason to use this.

You can register for application APIs here and can find the FAQ here.