May 302011
 

Last week I demonstrated designing and implementing customized layouts for Android ListView items, including some simple example code. This time around I’d like to expand on that code to make something a little closer to an actual app, illustrating some new tools and concepts in the process such as working with JSON data and the Android BitmapFactory.

In this post I’ll show you how to implement a Twitter feed reader for Android. Let’s start by familiarizing ourselves with the Twitter Search API Say we wanted to see what Twitter users have to say about Android today. A Twitter API search query would look something like:

http://search.twitter.com/search.json?q=@android

Twitter’s Search API allows up to 100 results to be returned per query, which is more than enough for our example today, but which introduces some interesting challenges as we want to display more data in a user-friendly fashion – more on that later. For today’s purposes, I’ll restrict return data to 25 results, using the following query:

http://search.twitter.com/search.json?q=@android&rpp=25&page=1

The “page” parameter can be incremented to step through chunks of 25 results in sequential queries. Go ahead and click on that link (will open a new window/tab) to check out the JSON data returned. There’s a lot of data values in there, but for simplicity we’ll be focusing on the following values:

  • from_user
  • text
  • profile_image_url

I’ll expand the simple example app from my last post to display twitter posts instead of a hard-coded list of users. The layout from the previous tutorial can be easily repurposed by changing the icon to the Twitter user’s avatar, placing the twitter username in the “username” field, and placing the tweet in the message field. Our goal is something like:

So how do we get there? Let’s go through it, step by step. First, we edit the listitem.xml file to give the View components more helpful names/IDs:



android:layout_height="wrap_content"
android:gravity="left|center"
android:layout_width="wrap_content"
android:paddingBottom="5px"
android:paddingTop="5px"
android:paddingLeft="5px">

android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="@drawable/icon" />
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10px"
android:textColor="#0099CC"/>


We can keep the main.xml from the previous post, since all we are changing is the layout within each item. Now we need some workhorse code to manage calling the Twitter API and formatting the return data for easy use. Let’s wrap our returned tweets in a simple Tweet class:


public class Tweet {
public String username;
public String message;
public String image_url;

public Tweet(String username, String message, String url) {
this.username = username;
this.message = message;
this.image_url = url;
}
}

I’ve also written a simple getTweets(searchTerm, page) method to generate the API call and process the return data into our Tweet objects. Here goes:


public ArrayList<Tweet> getTweets(String searchTerm, int page) {
String searchUrl =
"http://search.twitter.com/search.json?q=@"
+ searchTerm + "&rpp=100&page=" + page;</p>

ArrayList tweets =
new ArrayList();

HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(searchUrl);

ResponseHandler<String> responseHandler =
new BasicResponseHandler();

String responseBody = null;
try {
responseBody = client.execute(get, responseHandler);
} catch(Exception ex) {
ex.printStackTrace();
}

JSONObject jsonObject = null;
JSONParser parser=new JSONParser();

try {
Object obj = parser.parse(responseBody);
jsonObject=(JSONObject)obj;
}catch(Exception ex){
Log.v("TEST","Exception: " + ex.getMessage());
}

JSONArray arr = null;

try {
Object j = jsonObject.get("results");
arr = (JSONArray)j;
} catch(Exception ex){
Log.v("TEST","Exception: " + ex.getMessage());
}

for(Object t : arr) {
Tweet tweet = new Tweet(
((JSONObject)t).get("from_user").toString(),
((JSONObject)t).get("text").toString(),
((JSONObject)t).get("profile_image_url").toString()
);
tweets.add(tweet);
}

return tweets;
}

The code above is using the json-simple library, a nice lightweight tool for JSON encoding and decoding. I don’t have time now, but I may do some demos/examples using json-simple in a later post if there is interest. Aside from exception handling, all the code above does is formats the Twitter search request url from the input parameters “searchTerm” and “page”, makes an HTTP GET request using the generate url, parses the JSON return value, and stuffs the data into Tweet objects.

Notice that the Twitter user avatar data (profile_image_url) returned is simply a url to the user’s avatar. We need one more processing step to get the actual image, since Android ImageViews can’t display an image directly from the web without downloading it. (good thing too, or offline usage would be quite dull!) Let’s see how few lines we can do this in:


public Bitmap getBitmap(String bitmapUrl) {
try {
URL url = new URL(bitmapUrl);
return BitmapFactory.decodeStream(url.openConnection().getInputStream());
}
catch(Exception ex) {return null;}
}

May not break any records, but the code above is fairly compact. It uses another handy Android API tool called the BitmapFactory. The BitmapFactory lives and breathes bitmaps, its sole purpose in life being to return a bitmap from whatever source data you give it. File, filestream, byte array, you name it. Because of this, I don’t have to put any thought at all into bits, bytes, files, http requests, or anything else to accomplish this task. Rock on, BitmapFactory.

Now we have all of the tools we need to refactor the ItemAdapter from last week’s post to display our Tweet objects. Here we go:


public class TweetItemAdapter extends ArrayAdapter {
private ArrayList tweets;

public TweetItemAdapter(Context context, int textViewResourceId, ArrayList tweets) {
super(context, textViewResourceId, tweets);
this.tweets = tweets;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi =
(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.listitem, null);
}

Tweet tweet = tweets.get(position);
if (tweet != null) {
TextView username = (TextView) v.findViewById(R.id.username);
TextView message = (TextView) v.findViewById(R.id.message);
ImageView image = (ImageView) v.findViewById(R.id.avatar);

if (username != null) {
username.setText(tweet.username);
}

if(message != null) {
message.setText(tweet.message);
}

if(image != null) {
image.setImageBitmap(getBitmap(tweet.image_url));
}
}

return v;
}
}

Not too many changes from the original, but the changes we have made are important. Let’s go through them. No conceptual changes in the constructor, so I’ll skip that. getView() is where the magic happens. Now, since we are setting the avatar image independently for each user, we are grabbing an ImageView object as well as two TextView objects from our View. Since we already parsed the Twitter JSON data in the getTweets() method and packaged it nicely into our Tweet objects, all we need to do here is put the extracted values from the relevant Tweet object into the text field for our TextView objects. For the ImageView, we need to set a bitmap value, which is exactly what we wrote the getBitmap(url) method to return. That’s it!

For the sake of completeness, here is the onCreate() method for this example – this illustrates setting the adapter and passing some data to the ListView as our app is launched.


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);</p>

ArrayList tweets = getTweets("android", 1);

ListView listView = (ListView) findViewById(R.id.ListViewId);
listView.setAdapter(new TweetItemAdapter(this, R.layout.listitem, tweets));
}

For this example, we only ever get a single API call’s worth of data, a limitation I will address in a follow-up tutorial. Let’s take a screenshot of the finished product for posterity:

There you have it: a working Twitter feed reader, based on the Twitter search API. There are plenty of directions you could take this code, from allowing the user to specify search terms to changing the way data is aggregated or adding some persistent data features. If these are the types of things you are interested in, I hope this tutorial helps! Feel free to leave a comment or drop me a line if you have any questions. As always, you can download code using the links at the bottom of this post to try things out for yourself.

In my next post I’ll continue working on this Twitter app to add user scrolling detection and auto-expansion of the ListView, so we can continually add to the ListView as the user scrolls through. This is a handy trick that can be useful in many scenarios. Stay tuned!

Example.java listitem.xml main.xml

Get 50% off my Node.js course here

If you liked this article, help me out by sharing a 50% discount to my Node.js course here: Thanks!

You should follow me on Twitter here:

  • Scottyomalley

    Any chance you can change the code blocks to show all the code, I dispise scrolling within a page…

    • Anonymous

      I’m searching for a new theme with wider columns to solve that issue. Hopefully I find a workable one soon. Sorry for the inconvenience, but know that it will be fixed!

    • Anonymous

      Problem solved! With my traditional degree of overkill, I’ve revamped the site entirely. Now, in addition to having a wider column in which to display posts (which should take care of the majority of these scroll cases), I’ve also allowed a fluid width on the main column. So, if nothing else, you can now full screen the browser and get enough extra width in code blocks to encompass any reasonable width for code. Cheers!

  • http://twitter.com/Waza_Be Waza_be

    Nice stuff!

    Unfortunately, project cannot compile:

    At line: Object obj = parser.parse(responseBody);

    Eclipse tells me: The method parse(Reader) in the type JSONParser is not applicable for the arguments (String)

    Any idea?

    • Anonymous

      Thanks!

      My first guess is that you are declaring some other JSONParser object instead of the JSONParser from the json-simple library – unfortunately, it has the same class name as some others out there, they just reside in different namespaces.

      Make sure you have downloaded the json-simple library and included the jar as a dependency, and then check that the namespace on your declared object is: org.json.simple.parser.JSONParser.

      Let me know if this fixes it!

      • http://twitter.com/Waza_Be Waza_be

        This one just fixed everything:

        StringReader myStringReader = new StringReader(responseBody);

        • Anonymous

          So you are passing the StringReader into the JSONParser object now? Thanks for the update!

          I’ll look into it and see if they have changed the library since I posted this, and update the tutorial accordingly.

        • bttung

          Thanks it solved the problem!

      • John Bakalarczyk

        need json_simple-1.1.jar…. i had the same error with just json_simple.jar

    • Freic1989

      Any idea to solve this issue? i got the same problem too. can anyone tell me where i must put this code StringReader myStringReader = new StringReader(responseBody); ?

      • dude18

        having same problem .Where did you put the code-StringReader myStringReader = new StringReader(responseBody); ?

        also what jar did you use? json-simple or json-simple 1.1?

      • vinod

        just add json_simple-1.1.jar file in your project the application runs fine

  • Jatinpreet

    Hi, I just tried the code. The code is compiled without errors but the application force closes when run on the device. Any suggestions?

    This is the logcat:

    06-10 23:40:12.671: ERROR/AndroidRuntime(200): Uncaught handler: thread main exiting due to uncaught exception
    06-10 23:40:12.781: VERBOSE/MediaProvider(146): /sdcard volume ID: 383979293
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): java.lang.RuntimeException: Unable to start activity ComponentInfo{reade.feed.twitter/reade.feed.twitter.Feeds}: java.lang.NullPointerException
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread.access$2100(ActivityThread.java:116)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.os.Handler.dispatchMessage(Handler.java:99)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.os.Looper.loop(Looper.java:123)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread.main(ActivityThread.java:4203)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at java.lang.reflect.Method.invokeNative(Native Method)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at java.lang.reflect.Method.invoke(Method.java:521)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at dalvik.system.NativeStart.main(Native Method)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): Caused by: java.lang.NullPointerException
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at reade.feed.twitter.Feeds.getTweets(Feeds.java:133)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at reade.feed.twitter.Feeds.onCreate(Feeds.java:43)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
    06-10 23:40:12.812: ERROR/AndroidRuntime(200): … 11 more

    • Anonymous

      You have a null pointer exception in there. Can you run the debugger and see which object is null?

      • Jitsnik

        Hi, I tried running the application too using the debugger and I have an and exception “java.net.UnknownHostException: search.twitter.com ”

        at
        try{
        responseBody = client.execute(get, responseHandler);
        }catch(Exception ex) {
        ex.printStackTrace();
        }

        Kindly advice

        • http://www.codehenge.net Constantine Aaron Cois

          Looks like your app can’t communicate with search.twitter.com. Do you have internet permissions enabled in the manifest?

          Check your AndroidManifest.xml file and add:

          if its not there already.

          • Jitsnik

            Yeeiii !!!, it worked, thanks. yes that was the problem. Thanks a million, nice looking app too. Great work

          • Bferreira Info

            in my compile ok and i have the permission but

            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): java.lang.RuntimeException: Unable to start activity ComponentInfo{teste.teste1/teste.teste1.Example}: java.lang.NullPointerException
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1736)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1752)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread.access$1500(ActivityThread.java:123)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.os.Handler.dispatchMessage(Handler.java:99)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.os.Looper.loop(Looper.java:126)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread.main(ActivityThread.java:3997)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at java.lang.reflect.Method.invokeNative(Native Method)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at java.lang.reflect.Method.invoke(Method.java:491)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at dalvik.system.NativeStart.main(Native Method)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): Caused by: java.lang.NullPointerException
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at teste.teste1.Example.getTweets(Example.java:130)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at teste.teste1.Example.onCreate(Example.java:37)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1700)
            10-23 17:50:36.272: ERROR/AndroidRuntime(3571): … 11 more

            can u help plz?

          • http://www.facebook.com/tomasz.bochenek Tomek Bochenek

            Unfortunately I’m having the same issues as @Bferreira . Can anybody help us? All I can say about the null pointer is that it is returned somewhere during the adapter setting in onCreate…

          • http://www.facebook.com/tomasz.bochenek Tomek Bochenek

            OK, I’m gonna reply to myself – I’ve made one of those stupid mistakes – I had two similarly named layouts and choosen the wrong one…

          • Erwan Gereec

            I have the same issues as @Bferreira. Please tell me what exactly resolve the problem for you.

  • Roman Kl

    Thanks for useful information. It would be useful to know about Sigma Ukraine, IT outsourcing software development company that provides premium software development services. Also try cheapest homeowners insurance.

  • Markopolodev

    Thanks for the tutorial, very useful! I am trying to display tweets from one user. I am able to do this from twitter4j, but I am not able to modify your code to do it. As far as I can tell, simply substituting the URL should work, but it throws errors. Any ideas?

  • medevil13

    for(JSONObject t : arr)
    gives this error
    Can only iterate over an array or an instance of java.lang.Iterable.

    • medevil13

      Sorry fixed the problem
      wrong imports
      thanks for the tutorial thought

      • sx

        how do you solve this problem?

      • Jestinjoy

        How you fixed it? I am having the same problem

        • http://www.codehenge.net Constantine Aaron Cois

          I bet you are declaring some other JSONParser object instead of the JSONParser from the json-simple library – unfortunately, it has the same class name as some others out there, they just reside in different namespaces.

          Make sure you have downloaded the json-simple library and included the jar as a dependency, and then check that the namespace on your declared object is: org.json.simple.parser.JSONParser.

    • Dinakaran407

      add the following

      import org.json.simple.JSONArray;
      import org.json.simple.JSONObject;
      import org.json.simple.parser.JSONParser;

      download simple jason parser jar file 1.1

    • http://twitter.com/patilminal8 minal patil

      i was add simple jason parser jar into program but still get error on

      for(JSONObject t:arr)

      an only iterate over an array or an instance of java.lang.Iterable

      what to do??? plz replyyyyyyyyyy

  • jhoncw

    How do you implement a Row Click Listener that open the tweet url using the web browser?

  • borislemke

    Is there a complete guide/tutorial on making a fully functional twitter client for android? I mean something from OAuth to get tweets, mentions, notifications, message and so on.

  • Bpsingh

    great tutorial .
    thanks

  • http://twitter.com/Venom_Vendor Yoganandh

    I tried including json-library still i have error in this line

    JSONParser parser = new JSONParser();

    Error:
    Cannot instantiate the type JSONParser

    Can u plz explain me how to Overcome this

  • Six3sixweb

    For some reason,I’m only getting one Tweet result. Not sure why. Also, how do I make this so it shows just the timeline of one Twitter account?

    • Six3sixweb

      Nevermind the first issue. Fixed that. Still want to know how to display the timeline of just one user, though.

  • tux

    Getting error in the first line of following code:

    for(Object t : arr) {
    Tweet tweet = new Tweet(
    ((JSONObject)t).get(“from_user”).toString(),
    ((JSONObject)t).get(“text”).toString(),
    ((JSONObject)t).get(“profile_image_url”).toString()
    );
    tweets.add(tweet);
    }

    It says

    Can only iterate over an array or an instance of java.lang.Iterable.

    • Niels

      import org.json.simple.JSONArray;
      import org.json.simple.JSONObject;
      import org.json.simple.parser.JSONParser;

  • Durgeshpathakk

    i am getting error pls help me
    Can only iterate over an array or an instance of java.lang.Iterablefor(Object t : arr) {
    Tweet tweet = new Tweet(
    ((JSONObject)t).get(“from_user”).toString(),
    ((JSONObject)t).get(“text”).toString(),
    ((JSONObject)t).get(“profile_image_url”).toString()
    );
    tweets.add(tweet);
    }

    • http://www.codehenge.net Constantine Aaron Cois

      Looks like you need to include the json-simple libraries:

      import org.json.simple.JSONArray;
      import org.json.simple.JSONObject;
      import org.json.simple.parser.JSONParser;

  • Durgeshpathakk

    how to display the time of tweet pls send the code at my email id durgeshpathakk@gmail.com

    • http://www.codehenge.net Constantine Aaron Cois

      Hi Durgeshpathakk. Take a look at the Twitter Search API docs, or check out this sample query from the tutorial:

      http://search.twitter.com/search.json?q=@android

      In the results JSON you will see a created_at value for each tweet that shows the time it was created.

  • Durgeshpathakk

    sir i am not able do that please guide me to show time of tweets in my android emulator

  • http://www.facebook.com/lars.vandedonk Lars Van de Donk

    I used ((JSONObject)t).get(“created_at”).toString() to get the date but it displays like:
    Sat, 12 Nov 2011 22:03:42 +0000

    But I actually want it to display like : 13 hours ago like on twitter itself.
    Any ideas?

    • http://www.codehenge.net Constantine Aaron Cois

      Ya, just parse that string into a DateTime, and you should be able to set the display to whatever you want easily.

      • http://www.facebook.com/lars.vandedonk Lars Van de Donk

        Uhm, I’m quiet a noob with Java…
        Should it be something like

        ((JSONObject)t).get(“created_at”).toString(DateTime) ??

    • Iambalu Jampana

      U need to format the date to standard date format…becoz twitter replies date like jsva Script format…enjoy

    • Nandu

      Use this code you will get it exactly what u want..

      import java.text.SimpleDateFormat;
      import java.util.Date;
      import java.util.Locale;

      public class Post {
      public static String twitterHumanFriendlyDate(String dateStr) {
      // parse Twitter date
      SimpleDateFormat dateFormat = new SimpleDateFormat(
      “EEE, d MMM yyyy HH:mm:ss Z”, Locale.ENGLISH);
      dateFormat.setLenient(false);
      Date created = null;
      try {
      created = (Date) dateFormat.parse(dateStr);
      //created=(Date) dateFormat.parse(dateStr, null);
      } catch (Exception e) {
      return “Something”;
      }

      // today
      Date today = new Date();

      // how much time since (ms)
      Long duration = today.getTime() – created.getTime();

      int second = 1000;
      int minute = second * 60;
      int hour = minute * 60;
      int day = hour * 24;

      if (duration < second * 7) {
      return "right now";
      }

      if (duration < minute) {
      int n = (int) Math.floor(duration / second);
      return n + " seconds ago";
      }

      if (duration < minute * 2) {
      return "about 1 minute ago";
      }

      if (duration < hour) {
      int n = (int) Math.floor(duration / minute);
      return n + " minutes ago";
      }

      if (duration < hour * 2) {
      return "about 1 hour ago";
      }

      if (duration day && duration < day * 2) {
      return "yesterday";
      }

      if (duration < day * 365) {
      int n = (int) Math.floor(duration / day);
      return n + " days ago";
      } else {
      return "over a year ago";
      }
      }

      }

  • Duo

    This isn’t working for me unfortunately, can someone post a .ZIP of the folder containing it?

    • Bob

      I also want a zip file which does work!

  • Erwan Gereec

    Hello, I used the code in attachement and i have an error :

    E/AndroidRuntime(884): FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{fr.enstb.tp2/fr.enstb.tp2.TweetsActivity}: java.lang.NullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955)
    at android.app.ActivityThread.startActivityNow(ActivityThread.java:1796)
    at android.app.LocalActivityManager.moveToState(LocalActivityManager.java:135)
    at android.app.LocalActivityManager.startActivity(LocalActivityManager.java:347)
    at android.widget.TabHost$IntentContentStrategy.getContentView(TabHost.java:682)
    at android.widget.TabHost.setCurrentTab(TabHost.java:346)
    at android.widget.TabHost$2.onTabSelectionChanged(TabHost.java:150)
    at android.widget.TabWidget$TabClickListener.onClick(TabWidget.java:540)
    at android.view.View.performClick(View.java:3460)
    at android.view.View$PerformClick.run(View.java:13955)
    at android.os.Handler.handleCallback(Handler.java:605)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4340)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
    at dalvik.system.NativeStart.main(Native Method)
    Caused by: java.lang.NullPointerException
    at fr.enstb.tp2.TweetsActivity.getTweets(TweetsActivity.java:126)
    at fr.enstb.tp2.TweetsActivity.onCreate(TweetsActivity.java:36)
    at android.app.Activity.performCreate(Activity.java:4465)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919)
    … 18 more

    Please help me..

  • http://www.facebook.com/lars.vandedonk Lars Van de Donk

    I got a white background.
    But when I scroll it turns black, and when I hit one of the tweets it turns blue.
    Can I edit this with a layout XML? Or is there an other way?

    • Selim Namsi

      Hello ,
      to disable the black color when you scroll back , you have to modify the layout xml where you had defined your Listview and put this in the ListView:

      android:drawSelectorOnTop=”false”
      android:cacheColorHint=”#00000000″

      you can find more explanation here :http://android-developers.blogspot.com/2009/01/why-is-my-list-black-android.html

      and to change the blue color when you hit one of the tweets , you have to add this to the ListView in the layout XML

      android:listSelector=”put the color here”

  • Ramesh D

    i done it.. super one though

  • http://twitter.com/BinaryLaux BinaryLaux

    This Tutorial Helps Me A lot Thanks Cacois

    • http://www.codehenge.net Constantine Aaron Cois

      :)

    • Bob

      I know this is 2 years ago but I still ask this question: Can you please me your working codes to good_people@live.nl? thanks

  • Vijay

    the way you explain the example is really interesting. Great lines.. Thanks

  • ahmed ebeid

    I am just wondering .. where would I put a twitter name to retrieve only that user’s feeds, instead of just general feeds. Thanks

  • http://twitter.com/maharlikanglila Roljohn Villanueva

    How come some twitter accounts cant be parsed by this parsing method?
    btw thanks for the good tutorial :D

  • Girish

    Hey,
    I used the code in my program on clicking an image i swithched it to the main.xml above and then used your code. i only get a blank screen. Please help.

  • dipu

    hey,
    i have some problem in above code…
    i have getting error in json parser..

  • Mskudla

    Hey I am getting an error that says ListViewID can not be resolved or is not a type. R is auto-generated and I can’t declare anything in it so how do I declare this? Thanks

    ListView listView = (ListView) findViewById(R.id.ListViewId);

    • Mskudla

      Sorry, figured it out. I forgot to change my main.xml in this project to have the listview in it.

      However I still get a null pointer error at
      listView.setAdapter(new TweetItemAdapter(this, R.layout.listitem, tweets));

  • manahmanah

    Hey it works on 2.0.1 alright. but on 4.0.3 it doesn’t work. People tells about android.os.NetworkOnMainThreadException thing can you update it?

  • Vanngoc712

    Hey it works on 1.0 alright. but on 4.0.3 it doesn’t work. Please !

  • chu du

    It works on 1.6 alright, but on not 1.6 it doesn’t work.

  • Bonestarr2k

    This code doesn’t work for me, im running it on sdk version 7, and I have left the code identical to yours and I am able to compile but it closes unexpectedly and I get these errors:

    03-23 16:14:02.253: E/AndroidRuntime(215): Uncaught handler: thread main exiting due to uncaught exception

    03-23 16:14:02.263: E/AndroidRuntime(215): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.uwe.bus/com.uwe.bus.InfoActivity}: java.lang.NullPointerException
    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2496)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread.access$2200(ActivityThread.java:119)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.os.Handler.dispatchMessage(Handler.java:99)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.os.Looper.loop(Looper.java:123)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread.main(ActivityThread.java:4363)

    03-23 16:14:02.263: E/AndroidRuntime(215): at java.lang.reflect.Method.invokeNative(Native Method)

    03-23 16:14:02.263: E/AndroidRuntime(215): at java.lang.reflect.Method.invoke(Method.java:521)

    03-23 16:14:02.263: E/AndroidRuntime(215): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)

    03-23 16:14:02.263: E/AndroidRuntime(215): Caused by: java.lang.NullPointerException

    03-23 16:14:02.263: E/AndroidRuntime(215): at com.uwe.bus.InfoActivity.getTweets(InfoActivity.java:133)

    03-23 16:14:02.263: E/AndroidRuntime(215): at com.uwe.bus.InfoActivity.onCreate(InfoActivity.java:39)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)

    03-23 16:14:02.263: E/AndroidRuntime(215): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)

    03-23 16:14:02.263: E/AndroidRuntime(215): … 11 more

    Line 39:
    ArrayList tweets = null;
    try {
    tweets = getTweets(“android”, 1);
    } catch (JSONException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    Line 133:
    for(int i = 0; i < arr.length(); i++) {
    JSONObject t = (JSONObject) arr.get(i);
    Tweet tweet = new Tweet(
    t.get("from_user").toString(),
    t.get("text").toString(),
    t.get("profile_image_url").toString()
    );
    tweets.add(tweet);
    }

    I had to change those parts in order to compile, as I kept getting this error: Can only iterate over an array or an instance of java.lang.Iterable

    http://stackoverflow.com/questions/9839961/can-only-iterate-over-an-array-or-an-instance-of-java-lang-iterable

    Please help, thanks

    • http://www.codehenge.net Constantine Aaron Cois

      Ya, I think there may be some incompatibilities with the latest version. I’m working on debugging and pushing a new version out soon. When I do, I’ll put a note in this post to link to the new post I write. If anyone else comes up with any fixes, feel free to issue a pull request to github.

      Thanks for your patience!

      • Bonestarr2k

        That would be great as this was probably the best explained example online, its a shame that there was a few bugs, good luck fixing it and I hope you get it all fixed soon. Great work…!!!

  • ThomasPeters

    Great tutorial, only problem is, when I try to run this code for Ice Cream Sandwich it crashes for no apparent reason throwing an illegal exception error.

    Anyone else tried this with Android sdk level 15 and had success?

    • Bob

      I know it is two years ago, still asking this question: can you send me your working code to good_people@live.nl ? Please!

  • Albertorusso90

    Hi, thanks for the guide but I got a question, I been trying to run the code, everything its same as urs, but it doesnt work, it shows “Invalid cookie header: “Set-Cookie: k=89.98.39.128.3b2c85d516545ba5; path=/; expires=Thu, 03-May-2012 13:56:52 UTC; domain=.search.twitter.com; httponly”. Unable to parse expires attribute; Thu, 03-May-2012 13:56:52 UTC”

    Please let me know whats wrong…it just keeps crashing as soon as it runs, it looks like it is searching for the tweets but then app just crashes

  • Hemendraajmera1337

    hi,
    i want to implement rss reader application ,can u provide such king of complete tutorial and source code.
    Thanks in advanced’

  • Aaa

    The code compiled without any error. Then the Emulator gives this error. I think the path for the Jar file for the JSON library is correctly set up, I can not understand why I am getting this error :(

    Could not find class ‘org.json.simple.parser.JSONParser’, referenced from method

  • kennedynyaga

    I know this is much much later but if we all perhaps pitch in we could update it. So here is me first.

    1.) create JSONParser.java as follows

    package com.mikiko;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.UnsupportedEncodingException;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.json.JSONException;
    import org.json.JSONObject;
    import android.util.Log;
    public class JSONParser {

    static InputStream is = null;
    static JSONObject jObj = null;
    static String json = “”;

    // constructor
    public JSONParser() {

    }

    public JSONObject getJSONFromUrl(String url) {

    // Making HTTP request
    try {
    // defaultHttpClient
    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(url);

    HttpResponse httpResponse = httpClient.execute(httpPost);
    HttpEntity httpEntity = httpResponse.getEntity();
    is = httpEntity.getContent();

    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    } catch (ClientProtocolException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

    try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8);
    StringBuilder sb = new StringBuilder();
    String line = null;
    while ((line = reader.readLine()) != null) {
    sb.append(line + “n”);
    }
    is.close();
    json = sb.toString();
    } catch (Exception e) {
    Log.e(“Buffer Error”, “Error converting result ” + e.toString());
    }

    // try parse the string to a JSON object
    try {
    jObj = new JSONObject(json);
    } catch (JSONException e) {
    Log.e(“JSON Parser”, “Error parsing data ” + e.toString());
    }

    // return JSON String
    return jObj;

    }
    }

    2.) Then integrate it to the above code as follows

    package com.mikiko;

    import java.net.URL;
    import java.util.ArrayList;
    import org.json.JSONArray;
    import org.json.JSONObject;
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.TextView;

    public class MickoActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ArrayList tweets = getTweets(“ihub”, 20);

    ListView listView = (ListView) findViewById(R.id.listView01);
    listView.setAdapter(new UserItemAdapter(this, R.layout.listitem, tweets));
    }

    public class UserItemAdapter extends ArrayAdapter {
    private ArrayList tweets;

    public UserItemAdapter(Context context, int textViewResourceId, ArrayList tweets) {
    super(context, textViewResourceId, tweets);
    this.tweets = tweets;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
    LayoutInflater vi = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    v = vi.inflate(R.layout.listitem, null);
    }

    Tweet tweet = tweets.get(position);
    if (tweet != null) {
    TextView username = (TextView) v.findViewById(R.id.username);
    TextView message = (TextView) v.findViewById(R.id.message);
    ImageView image = (ImageView) v.findViewById(R.id.avatar);

    if (username != null) {
    username.setText(tweet.username);
    }

    if(message != null) {
    message.setText(tweet.message);
    }

    if(image != null) {
    image.setImageBitmap(getBitmap(tweet.image_url));
    }
    }
    return v;
    }
    }

    public Bitmap getBitmap(String bitmapUrl) {
    try {
    URL url = new URL(bitmapUrl);
    return BitmapFactory.decodeStream(url.openConnection() .getInputStream());
    }
    catch(Exception ex) {return null;}
    }

    public ArrayList getTweets(String searchTerm, int page) {
    String URL =”http://search.twitter.com/search.json?q=@”+searchTerm;
    ArrayList tweets = new ArrayList();
    JSONParser jParser = new JSONParser();
    JSONObject json = jParser.getJSONFromUrl(URL);

    JSONArray ja = null;
    try{
    ja = json.getJSONArray(“results”);
    for (int i = 0; i < ja.length(); i++) {
    Tweet tweet = new Tweet(
    ((JSONObject) ja.get(i)).get("from_user").toString(),
    ((JSONObject) ja.get(i)).get("text").toString(),
    ((JSONObject) ja.get(i)).get("profile_image_url").toString()
    );

    tweets.add(tweet);
    }
    }catch(Exception e){

    }

    return tweets;
    }

    public class Tweet {
    public String username;
    public String message;
    public String image_url;

    public Tweet(String username, String message, String url) {
    this.username = username;
    this.message = message;
    this.image_url = url;
    }
    }
    }

    • RunningCoder

      Have you actually got it running with this code?

    • Nidhi Gupta

      hi. i used ur code but it gives error in logcat..

      08-27 03:27:35.762: W/dalvikvm(3754): threadid=1: thread exiting with uncaught exception (group=0x414c4700)

      08-27 03:27:35.822: E/AndroidRuntime(3754): FATAL EXCEPTION: main

      08-27 03:27:35.822: E/AndroidRuntime(3754): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.twitter_demo_project/com.twitter_demo_project.TweetsListActivity}: java.lang.RuntimeException: Your content must have a ListView whose id attribute is ‘android.R.id.list’

      08-27 03:27:35.822: E/AndroidRuntime(3754): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)

      08-27 03:27:35.822: E/AndroidRuntime(3754): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)

      08-27 03:27:35.822: E/AndroidRuntime(3754): at android.app.ActivityThread.access$600(ActivityThread.java:141)

    • evab

      remove the setcontentview()

  • Yuri

    Thanks for the tutorial. But I have a doubt: if I want only appear tweets made ​​by the author of the account I have to change the url?
    http://search.twitter.com/search.json?q = @ android & rpp = 25 & page = 1

    Can you do that?

  • nickolang

    is this working?

  • Frank Jr 619

    i wanna ask, if i display image like that you do, when i scroll it , it very2 lag. how to solve that problem?

  • CuriousAndry

    Very nice. Thanks for this.

  • Hendi Ananta

    how about with twitter feed reader with Get the timeline of user

  • LittleGirl

    Good afternoon,

    As this application can run with the modification of the api v1.1.

    If I try to access: http://search.twitter.com/search.json?q = @ android

    I skip the following error:

    {“errors”: [{"message": "The Twitter REST API v1 is no longer active. Please migrate to API v1.1. https://dev.twitter.com/docs/api/1.1/overview." "code": 68}]}

    For they understand that you must change this.

    Someone please help me? thanks

  • Bob

    Can any of you who could run this project successfully, share thier codes as zip file? Please?

  • andrewdmackenzie

    The search described seems to be API v1 at Twitter and now deprecated.
    http://search.twitter.com/search.json?q=@android

43 queries in 2.189 seconds