When Twitter released its Live Streaming API about a year and a half ago, the first application I created was a multi-part C# desktop app for pulling down filtered tweets and logging them to a database for building reports and data mining. I recently decided that it might be helpful to provide a sample Android application that could utilize Twitter’s Live Streaming API.
The UI for this sample is simple: one button for starting and stopping the stream, one text field for entering a filter term, and a ListView for displaying the results.
The layout
The following is the XML layout I used for this sample:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/StartStopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Start"
android:onClick="startStop" />
<EditText
android:id="@+id/SearchText"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</EditText>
<ListView android:id="@+id/Tweets"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
></ListView>
</LinearLayout>
onCreate
You will need a class-wide List of HashMaps that will act as the storage container for the streaming tweets, as well as a handle to the ListView’s list adapter. So create the following two class-wide instance variables:
private List<HashMap<String,String>> mTweets = new ArrayList<HashMap<String,String>>(); private SimpleAdapter mAdapter;
You will also want some way to track the current Start/Stop state of the stream, as well as the current search term, so add the following two instance variables:
private boolean mKeepRunning = false; private String mSearchTerm = "";
Then, in the onCreate method of your activity, instantiate the list adapter and set it for the ListView:
@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAdapter = new SimpleAdapter(this, mTweets, android.R.layout.simple_list_item_2, new String[] {"Tweet", "From"}, new int[] {android.R.id.text1, android.R.id.text2});
((ListView)findViewById(R.id.Tweets)).setAdapter(mAdapter);
}
Asynchronous execution
The tweets must be streamed asynchronously in order to be able to smoothly update the UI as they come through the stream. In order to do this, the AsyncTask class is a perfect candidate because we can do the work in the doInBackground method and update the UI from the onProgressUpdate method since it is executed on the UI thread.
First, let’s look at the doInBackground method.
@Overrideprotected Integer doInBackground(Integer... params) {
try {
DefaultHttpClient client = new DefaultHttpClient();
Credentials creds = new UsernamePasswordCredentials(userName, password);
client.getCredentialsProvider().setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), creds);
HttpGet request = new HttpGet();
request.setURI(new URI("https://stream.twitter.com/1/statuses/filter.json?track=" + mSearchTerm));
HttpResponse response = client.execute(request);
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader( new InputStreamReader(in) );
parseTweets(reader);
in.close();
}
catch (Exception e) {
Log.e("Twitter", "doInBackground_" + e.toString());
}
return new Integer(1);
}
We start by establishing a connection to Twitter using the Apache HttpClient class and then retrieve a handle to the InputStream object. Then, grab a BufferedReader so the input can be read line by line.
Next we have to deal with parsing the tweets as they come in. We are going to:
- Read a line from the BufferedReader.
- Create a JSONObject with the text.
- Create a HashMap with data from the JSONObject.
private void parseTweets( BufferedReader reader ) {try {
String line = "";
do {
line = reader.readLine();
JSONObject tweet = new JSONObject(line);
HashMap<String, String> tweetMap = new HashMap<String, String>();
if (tweet.has("text")) {
tweetMap.put("Tweet", tweet.getString("text"));
tweetMap.put("From", tweet.getJSONObject("user") .getString("screen_name"));
mTweets.add(0, tweetMap);
if (mTweets.size() > 20) {
mTweets.remove(mTweets.size() - 1);
}
publishProgress(1);
}
} while (mKeepRunning && line.length() > 0);
}
catch (Exception e) {
// TODO: handle exception
}
}
I’m only parsing the tweet if it contains a “text” key because sometimes items will come through that don’t contain text (e.g., “Deletions”). Since no data is being stored in this example, it’s safe to ignore these items.
You’ll notice that I’m only keeping a list of the last 20 tweets in memory. The reason why is tweets come in so fast on particular queries that we can easily run out of memory if we are not careful about limiting the list.
After the tweet is parsed, we call the publishProgress method of our AsyncTask, which will trigger the onProgressUpdated method. It doesn’t matter what value you pass to publishProgress since the only thing that needs to be done in onProgressUpdated is to notify the list adapter that the data set has changed:
mAdapter.notifyDataSetChanged();
Starting and stopping
In order to start and stop the streaming, we can use the onClick handler of the StartStop button from the layout earlier:
public void startStop( View v ) {if( ((Button)v).getText().equals("Start") ) {
mSearchTerm = ((EditText)findViewById(R.id.SearchText)).getText().toString();
if( mSearchTerm.length() > 0 ) {
new StreamTask().execute();
mKeepRunning = true;
((Button)v).setText("Stop");
}
else {Toast.makeText(this, "You must fill in a search term", Toast.LENGTH_SHORT).show();
}
}
else {
mKeepRunning = false;
((Button)v).setText("Start");
}
}
To keep things simple, we can rely on the current text value of the Start/Stop button to determine which logic to execute.
If the streaming is currently stopped and the user has entered in a search term, then call the execute method of your AsyncClass after properly setting your class-wide instance variables.
Otherwise, setting mKeepRunning to false will stop the streaming the next time a line is read.
Conclusion
If you’ve been wanting to play around with the Twitter Live Streaming API but it looked a bit advanced, I hope this basic example will help you get you on your way to building a better, more innovative application.