Friday, August 13, 2010

Save image in instance state (Android)

While working on an app, I had an activity that downloaded an image from the Internet and displayed it to the user (amongst other things). Because all of the data presented to the user is downloaded from the Internet, it's mandatory for me to save this data in onSaveInstanceState() and restore it in onCreate().

At first, I had a problem. I was downloading the image to a Drawable, using Drawable.createFromStream(). Now, because a Drawable is dependent on its Context, it can't be serialized or parceled.

The solution was to use a Bitmap instead, which thankfully implements Parcelable. Downloading a Bitmap is easy:

URL url = new URL("http://www.example.com/pic.jpg"); Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());

Then saving it in the instance state is even easier:

public void onSaveInstanceState(Bundle outState) { outState.putParcelable("bitmap", bitmap); }

Restoring:

if (savedInstanceState != null) bitmap = (Bitmap) savedInstanceState.getParcelable("bitmap");

And there you have it, simple as pie.

Friday, July 30, 2010

Android Title Marquee

I recently wanted the title of my activity to scroll like a marquee whenever it was too long to fit in its space (because it is fetched from the net and can be pretty much anything). I didn't want to use a custom multi-line title view because that would waste screen space and because I want to preserve the native look and feel. The solution I've found is rather hackish, but it works. Here goes the code, with explanatory comments and all:

// make the title scroll! // find the title TextView TextView title = (TextView) findViewById(android.R.id.title); // set the ellipsize mode to MARQUEE and make it scroll only once title.setEllipsize(TruncateAt.MARQUEE); title.setMarqueeRepeatLimit(1); // in order to start strolling, it has to be focusable and focused title.setFocusable(true); title.setFocusableInTouchMode(true); title.requestFocus();

Saturday, June 5, 2010

Java vs. Python: fetching URLs

Here we go, another Java vs. Python comparison (I just can't help myself). This time it's about standard library usefulness in doing certain tasks. Fetching the contents of a URL should be a trivial one, but in Java, it's not. Especially if the contents of that URL are gzipped and use a nice charset such as UTF-8.

Java:

URL url = new URL("http://www.example.com/"); URLConnection conn = url.openConnection(); conn.connect(); InputStream in; if (conn.getContentEncoding().equals("gzip")) { in = new GZIPInputStream(conn.getInputStream()); } else { in = conn.getInputStream(); } String charset = conn.getContentType(); BufferedReader reader; if (charset.indexOf("charset=") != -1) { charset = charset.substring(charset.indexOf("charset=") + 8); reader = new BufferedReader(new InputStreamReader(in, charset)); } else { charset = null; reader = new BufferedReader(new InputStreamReader(in)); } StringBuilder builder = new StringBuilder(); String line = reader.readLine(); while (line != null) { builder.append(line + '\n'); line = reader.readLine(); } String content = builder.toString(); // FINALLY!

Python:

content = urllib2.urlopen("http://www.example.com/").read()

At first I though yeah, well, Java is probably older and wasn't designed to do such things very often. I was wrong. Java appeared in 1995, Python in 1991.

Tuesday, June 1, 2010

Java "Hello, World!" 6x slower than Python

Yup. I know, micro-benchmarks, but I find this one quite interesting. Take two small programs, one Java, one Python:

package com.test; public class TestJava { public static void main(String[] args) throws Exception { System.out.println("Hello, World!"); } } print "Hello, World!"

Running these two through time clearly shows that Java is 6 (six) times slower:

[felix@the-machine bin]$ time java com.test.TestJava Hello, World! real 0m0.142s user 0m0.080s sys 0m0.017s [felix@the-machine bin]$ time java com.test.TestJava Hello, World! real 0m0.142s user 0m0.077s sys 0m0.020s [felix@the-machine bin]$ time java com.test.TestJava Hello, World! real 0m0.154s user 0m0.070s sys 0m0.023s [felix@the-machine python]$ time python test.py Hello, World! real 0m0.024s user 0m0.013s sys 0m0.007s [felix@the-machine python]$ time python test.py Hello, World! real 0m0.025s user 0m0.020s sys 0m0.003s [felix@the-machine python]$ time python test.py Hello, World! real 0m0.026s user 0m0.013s sys 0m0.007s

Not only is Java slower, it's also a lot more code. And I'm not only talking about lines of code here (which, still, are 6 times more; really it's 1 line of Python and 6 of Java -- if you remove the whitespace), but compiled code, too. Look:

[felix@the-machine bin]$ ls -lh com/test/TestJava.class -rw-r--r-- 1 felix felix 595 Jun 1 21:50 com/test/TestJava.class [felix@the-machine python]$ ls -lh test.pyc -rw-r--r-- 1 felix felix 117 Jun 1 21:59 test.pyc

The compiled Java code is 595 bytes, while the compiled Python code is 117 bytes. Five times bigger. Say it with me: Java == BLOAT !

Monday, May 24, 2010

Server 'kitty' shut down

I used to have a home server which I playfully named kitty, mainly because it was not very powerful, yet it was very fun to play with. Today, I shut it down to replace it with a newer machine. These were its last words:

Broadcast message from felix@kitty (/dev/pts/0) at 12:00 ... The system is going down for power off NOW! Meow? :( felix@kitty:~$ Connection to kitty closed by remote host.

:(

Thursday, April 22, 2010

Android YouTube Intent

For a long time I've wondered how to show a YouTube video to the user in an Android application. There's this awesome post published by KeyesLabs on how to create your own Activity that plays YouTube videos. It's great, and you should definitely use it. But I think you can improve on that. It would be very useful for the user to view that video in the default YouTube player installed on the device because this way they can save it (like it, rate it, save it to their profile) plus enjoy other improvements and features the official YouTube app provides (plus probably better error checking for unavailable videos and so on).

While I was playing around with the emulator, I noticed that if you try to view a YouTube video in it th browser gives an error similar to Cannot open the page at vnd.youtube:VIDEO_ID?some=other¶meters=here. This way, I learned that a VIEW intent with a data URI like vnd.youtube:VIDEO_ID will launch the official YouTube app (this was confirmed by some nice folks on IRC, as I don't have an Android device). Basically:

Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:VIDEO_ID")); startActivity(i);

Will launch the YouTube app and watch the video with ID VIDEO_ID. Couple this with the Activity on KeyesLabs' blog and the Can I use this Intent? article and you've got a winner. My final solution is:

private void startVideo(String videoID) { // default youtube app Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + videoID)); List<ResolveInfo> list = getPackageManager().queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY); if (list.size() == 0) { // default youtube app not present or doesn't conform to the standard we know // use our own activity i = new Intent(getApplicationContext(), YouTube.class); i.putExtra("VIDEO_ID", videoID); } startActivity(i); }

Wednesday, April 14, 2010

How-to: Android Favorite Button (the right way, this time)

My last blog post is about how to make a favorite button in an Android application. It is wrong, those drawables should not be used independently. Instead, the @android:drawable/btn_star resource should be used, as it is a state-drawable and contains all the possible states (checked, unchecked, checked and focused, checked and clicked, ...). The proper way to use this is:

<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@android:drawable/btn_star"/>

The result looks exactly like the button used in Android's Contacts application when viewing a contact (upper-right corner).

Man, that took some digging.