Friday, July 23, 2010

The Curious Case of the Missing HttpsURLConnection

I ran into an Android OS bug recently that is pretty harsh related to HTTPS connections. Basically, what happens is this:

1. You want to setup a connection between the phone and a server, and you need to control both the input and the output. As a result, you use URL.openConnection(), with setDoInput() and setDoOutput() set to true:

URL url = new URL("https://blahblahblah.com");
URLConnection conn = url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);


At some point you use both conn.getOutputStream() to write to the stream, then conn.getInputStream() to get the response.

2. You're doing an HTTPS connection. Some people report this happening on normal HTTP, but I've only seen it happen on HTTPS.

3. The first request goes through fine and dandy.

4. The second time you try to make the request, the connection doesn't send any data out and doesn't receive any data; it looks like it happens instantly. If you cast to an HttpURLConnection, conn.getResponseCode() returns -1 instead of anything meaningful.

In other words, every other request, the request fails outright. This is a noted bug in Android, but it isn't fixed yet in any released versions. Even when it's fixed, you'll still have to deal with this on older versions of Android.

There are a few workarounds. The first is to simply not use URLConnection; if you can find some way around it, avoid it. The second is to repeatedly make the same request until it works; it's a little too much of a hack for my tastes, but it'll work.

Then there's the third workaround, which I do not claim to understand why it fixes the issue but it does. Just set this setting when your application begins:

System.setProperty("http.keepAlive", "false");


Unfortunately this has some drawbacks (keep-alive is a good thing normally), but in comparison to mysteriously failed requests I'll ditch it.

Thursday, July 15, 2010

How to Change a ListView Row's Background but Keep Normal Android Selector

Here's a problem which I thought would be super complex to solve but is actually rather simple. The solution assumes a bit of knowledge of ListView row types, though; if you've never dealt with them, here's a primer.

Suppose I want to make a ListView composed of similar rows with different backgrounds. This is simple enough; create a ListView with multiple item view types and then as each one loads, set a different background resource based on their type. The catch is that you want the selected/pressed graphics to still look the same. An example of this would be an email app; you want read and unread emails to look different, but when the item is selected or pressed you still want that tacky orange (or whatever your system uses).

The naive solution of simply setting the background resource does not work, because it completely blocks the standard selector:

// Bad; don't use this!
convertView.setBackgroundResrouce(R.color.some_color);


I was able to glean the correct answer by examining the Email app's source code (thank goodness for open source). What you want to do is create a background which is transparent whenever the list's background selector kicks in, but uses your custom background color (or resource) when it's not. Here's what the drawable xml looks like:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_selected="true" android:drawable="@android:color/transparent" />
<item android:state_selected="true" android:drawable="@android:color/transparent" />
<item android:state_pressed="true" android:state_selected="false" android:drawable="@android:color/transparent" />
<item android:state_selected="false" android:drawable="@color/some_color" />
</selector>


With this in hand, you can set your view's background like so:

convertView.setBackgroundResrouce(R.drawable.some_row_background);


Just apply the above XML to multiple files, and you'll be able to set different types of backgrounds but still preserve the ListView's selector background.