Working with URLConnection and Timeouts

One of the problems I encountered whilst developing my first Android app was getting a call to a web service to timeout if no response was received after around 10 seconds. Being primarily a .Net developer I was not too familiar with the various libraries and APIs of Java and Android. However it didn’t take long to find the URL and URLConnection classes. My first attempt used the URL class calling the openStream method and no timeout whatsoever:


private String getResponse(URL url){

     BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));

}

This worked to a point but when, for whatever reason, an attempt to connect failed my app would just sit there forever. Of course, my code was inside an AsyncTask so the UI was never blocked but the task would never complete. So I turned my attention to the URLConnection class and found the setConnectTimeout method.


private String getResponse(URL url){

     // given a url open a connection
     URLConnection c = url.openConnection();

     // set the connection timeout to 5 seconds
     c.setConnectTimeout(5000);

     // get a stream to read data from
     BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));

     // use the stream...

}

This time I created a URLConnection instance from the url passed to the method, set the timeout and then called openStream. I expected this to work but no. However, it turns out I had made a subtle mistake. Look at the last line and notice that I get a stream from the url instance. Seems a reasonable thing to do. However, doing it this way means the call to setConnectTimeout is ignored. If you create a URLConnection instance and set a timeout period then you also need to get a stream from the resulting connection object like this:


private String getResponse(URL url){

     // given a url open a connection
     URLConnection c = url.openConnection();

     // set the connection timeout to 5 seconds
     c.setConnectTimeout(5000);

     // get a stream to read data from but this time from the connection instance
     BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));

     // use the stream...

}

This is the correct way to do it but I still had a problem with the connection not timing out on occasion. The missing piece was a call to setReadTimeout:


private String getResponse(URL url){

     // given a url open a connection
     URLConnection c = url.openConnection();

     // set the connection timeout to 5 seconds and the read timeout to 10 seconds
     c.setConnectTimeout(5000);
     c.setReadTimeout(10000);

     // get a stream to read data from
     BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));

     // use the stream...

}

The setConnectTimeout method is used in establishing a connection to the url whilst setReadTimeout is used in reading data on an already established connection, therefore there are two different reasons for a timeout to occur. You need to set explicit values for both as by default they are set to zero meaning they will never timeout. Either one could fail and your method would never return. With both timeouts set the code works as you would expect.

Advertisements
Working with URLConnection and Timeouts

Create an Android Dialog with an Image

Want something more than text when you pop up a dialog from your Android app? How about a company logo inside your about box? It’s not hard at all. In this post, I show how to create a menu option for an about box and how to launch a dialog with a custom view when its selected.

First of all, we create an xml layout file for our about dialog and call it about.xml:

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:id="@+id/root"
  android:padding="10dip">

<ImageView
  android:id="@+id/about_logo"
  android:layout_width="40dip"
  android:layout_height="40dip"
  android:layout_margin="10dip"
  android:layout_gravity="center"
  android:layout_centerHorizontal="true"
  android:src="@drawable/icon"
  />

<TextView android:id="@+id/about_title"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:text="@string/about_content"
  android:gravity="center_horizontal"
  android:layout_centerHorizontal="true"
  android:layout_below="@id/about_logo"
  />
</RelativeLayout>

This layout contains a RelativeLayout root which means we can a bit more precise with how elements are positioned compared to the LinearLayout where items are stacked side by side either horizontally or vertically depending on the chosen android:orientation setting. Having said that, I’m not doing anything special by using the RelativeLayout here. In fact, I could have achieved the same layout with the LinearLayout but I try to use RelativeLayout where possible as I believe it’s more efficient when rendering, especially when nesting layouts. In this file we have an ImageView that will display the company logo and a TextView that will display the copyright blurb. The actual text and image displayed are resources in the application designated by android:src=”@drawable/icon” and android:text=”@string/about_content” respectively.

Next up, we need a menu that will appear when the Menu button of an Android phone is tapped. Again the content of a menu is determined by an xml file that lives in the res/menu folder named menu.xml:

<?xml version="1.0" encoding="UTF-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/menuAbout"
  android:title="About..."
  android:alphabeticShortcut="a"
  />
</menu>

Having defined our layouts and menus let’s turn to the code. In our Activity we override the onCreateOptionsMenu. When invoked the MenuInflater creates the UI element to display our “About…”  menu option using the identifier R.menu.menu, which corresponds to menu.xml

@Override
public boolean onCreateOptionsMenu(Menu menu){
  super.onCreateOptionsMenu(menu);
  MenuInflater inflater = getMenuInflater();
  inflater.inflate(R.menu.menu, menu);
  return true;
}

In order to respond to the menu item being selected we also need to override the onOptionsItemSelected method and this is where we show our dialog with our image:

@Override
public boolean onOptionsItemSelected(MenuItem item){
  super.onOptionsItemSelected(item);
  switch(item.getItemId()){
  case R.id.menuAbout:
    LayoutInflater inflater = (LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
    View layout = inflater.inflate(R.layout.about, (ViewGroup)findViewById(R.id.root));
    AlertDialog.Builder adb = new AlertDialog.Builder(this);
    adb.setView(layout);
    adb.show();
    return true;
  }
  return false;
}

The switch statement only shows the one option (I removed others for clarity) but we could easliy have more which is why we need to know which one was selected hence the call to getItemId() on the MenuItem object which is passed to us by the runtime. Once we determine that the About option was selected we need to turn our xml file, which we declared at the beginning, into a full fledged view instance. This is the job of the LayoutInflater which we grab using the getSystemService method call. Next, the inflate method is invoked passing the id of the layout file that we want to instantiate. This id is automatically generated for us based on the name of the file – about.xml. The second parameter corresponds to the id we gave to the RelativeLayout element in the same file. The end result is a View object which we can pass to the AlertDialog.Builder through the setView method. Finally, when we call show() the dialog is displayed with our image:

About

Summary

Displaying a dialog with an image essentially comes down to creating a layout file, using LayoutInflater to create an instance, and attaching that instance to the AlertDialog. With a custom layout file your dialog can be as rich as you want.

Create an Android Dialog with an Image