Android 4
23. HTTP Internet Connection
This chapter
- 23.0 HTTP Connection
- 23.1 HTTP Connection with Handler - Example 1
- 23.2 HTTP Connection with Handler - Example 2
How do we set up internet connection from our Android activity?
There is nothing new the way we make HTTP connection in Android platform compared with regular application written in core Java. However, in this tutorial, in additon to the Http Connection, we will see how the communication is done between thread and the Message object.In this example, we download an image and text from the web.
Let's preview the whole Java code first, HttpURLConncecitonA.java:
package com.bogotobogo.httpconnecta; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; public class HttpURLConnectionA extends Activity { private ProgressDialog progressDialog; private Bitmap bitmap = null; private String text = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button imageBtn = (Button)findViewById(R.id.Button01); Button textBtn = (Button)findViewById(R.id.Button02); imageBtn.setOnClickListener( new OnClickListener() { public void onClick(View v) { downloadImage("http://www.bogotobogo.com/images/smalltiger.gif"); } }); textBtn.setOnClickListener(new OnClickListener() { public void onClick(View v) { downloadText("http://www.bogotobogo.com/Android/android.php"); } }); } private void downloadImage(String urlStr) { progressDialog = ProgressDialog.show(this, "", "Downloading Image from " + urlStr); final String url = urlStr; new Thread() { public void run() { InputStream in = null; Message msg = Message.obtain(); msg.what = 1; try { in = openHttpConnection(url); bitmap = BitmapFactory.decodeStream(in); Bundle b = new Bundle(); b.putParcelable("bitmap", bitmap); msg.setData(b); in.close(); } catch (IOException e1) { e1.printStackTrace(); } messageHandler.sendMessage(msg); } }.start(); } private void downloadText(String urlStr) { progressDialog = ProgressDialog.show(this, "", "Download Text from " + urlStr); final String url = urlStr; new Thread () { public void run() { int BUFFER_SIZE = 2000; InputStream in = null; Message msg = Message.obtain(); msg.what=2; try { in = openHttpConnection(url); InputStreamReader isr = new InputStreamReader(in); int charRead; text = ""; char[] inputBuffer = new char[BUFFER_SIZE]; while ((charRead = isr.read(inputBuffer))>0) { String readString = String.copyValueOf(inputBuffer, 0, charRead); text += readString; inputBuffer = new char[BUFFER_SIZE]; } Bundle b = new Bundle(); b.putString("text", text); msg.setData(b); in.close(); } catch (IOException e2) { e2.printStackTrace(); } messageHandler.sendMessage(msg); } }.start(); } private InputStream openHttpConnection(String urlStr) { InputStream in = null; int resCode = -1; try { URL url = new URL(urlStr); URLConnection urlConn = url.openConnection(); if (!(urlConn instanceof HttpURLConnection)) { throw new IOException ("URL is not an Http URL"); } HttpURLConnection httpConn = (HttpURLConnection)urlConn; httpConn.setAllowUserInteraction(false); httpConn.setInstanceFollowRedirects(true); httpConn.setRequestMethod("GET"); httpConn.connect(); resCode = httpConn.getResponseCode(); if (resCode == HttpURLConnection.HTTP_OK) { in = httpConn.getInputStream(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return in; } private Handler messageHandler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: ImageView img = (ImageView) findViewById(R.id.imageview01); img.setImageBitmap((Bitmap)(msg.getData().getParcelable("bitmap"))); break; case 2: TextView text = (TextView) findViewById(R.id.textview01); text.setText(msg.getData().getString("text")); break; } progressDialog.dismiss(); } }; }
Since retrieving data from the internet can be a time consuming, we use a separate thread to get the image file. We should not use UI/main thread doing this kind of task as we discussed in the 22. Threads.
How do we get a connection when we press the Download button?
At the click of a button, Download Image, we first open an HTTP connection to the server and then request for the image. Here is the our method, openHttpConnection(String urlStr), just for that task. Basically it is our utility code for retrieving image/text from the internet.
private InputStream openHttpConnection(String urlStr) { InputStream in = null; int resCode = -1; try { URL url = new URL(urlStr); URLConnection urlConn = url.openConnection(); if (!(urlConn instanceof HttpURLConnection)) { throw new IOException ("URL is not an Http URL"); } HttpURLConnection httpConn = (HttpURLConnection)urlConn; httpConn.setAllowUserInteraction(false); httpConn.setInstanceFollowRedirects(true); httpConn.setRequestMethod("GET"); httpConn.connect(); resCode = httpConn.getResponseCode(); if (resCode == HttpURLConnection.HTTP_OK) { in = httpConn.getInputStream(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return in; }
Here, we open a URLConnection
URLConnection urlConn = url.openConnection();
Then, checks if it is an instance of HttpURLConnection
if (!(urlConn instanceof HttpURLConnection)) { throw new IOException ("URL is not an Http URL"); }
then, sets the parameters required
HttpURLConnection httpConn = (HttpURLConnection)urlConn; httpConn.setAllowUserInteraction(false); httpConn.setInstanceFollowRedirects(true); httpConn.setRequestMethod("GET");
and made the connection by calling the connect() method.
httpConn.connect();
Then, we check if the response code is OK and I get a handle to the input stream.
resCode = httpConn.getResponseCode(); if (resCode == HttpURLConnection.HTTP_OK) { in = httpConn.getInputStream(); }
Now, we know how we are connected to the internet.
And we get input stream through the connection using a hard coded URL of our image.
Let's step back a little bit, and check how we feed that URL at the click of Download Image button.
Button imageBtn = (Button)findViewById(R.id.Button01); ... imageBtn.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { downloadImage("http://www.bogotobogo.com/images/smalltiger.gif"); } });
So, actually downloading an image is done through our method, downloadImage(String url).
private void downloadImage(String urlStr) { progressDialog = ProgressDialog.show(this, "", "Downloading Image from " + urlStr); final String url = urlStr; new Thread() { public void run() { InputStream in = null; Message msg = Message.obtain(); msg.what = 1; try { in = openHttpConnection(url); bitmap = BitmapFactory.decodeStream(in); Bundle b = new Bundle(); b.putParcelable("bitmap", bitmap); msg.setData(b); in.close(); } catch (IOException e1) { e1.printStackTrace(); } messageHandler.sendMessage(msg); } }.start(); }
If we are not getting the image in a separate thread, we only need the three lines of code.
in = openHttpConnection(url); bitmap = BitmapFactory.decodeStream(in); ...... in.close();
Open a connection, get the bitmap and close the connection. However, since this is a typical task with unpredictable response time, it should be done in a separate thread of its own.
So, before we start a new thread, we let the UI thread to show a ProgressDialog for our impatient user.
progressDialog = ProgressDialog.show(this, "", "Downloading Image from " + urlStr);
Then, we start a new thread. we make the URL string accessible within the new thread by making it a final variable. Since message is an object that we can use for communication between threads, we create a Message object in the thread. Then, we set the message number to 1, so that we can use it later.
Message msg = Message.obtain(); msg.what = 1;
Then, we bundle our bitmap already put into a Bundle object that can be sent back in the Message object.
Bundle b = new Bundle(); b.putParcelable("bitmap", bitmap); msg.setData(b);
Once we close the input stream as in:
in.close();
the thread has completed the job. So, we notify the main / UI thread through this method and also pass on the Message object:
messageHandler.sendMessage(msg);
Now we got the image through a Message in a separate thread.
But, how do we retrieve the image from the Message object in the main thread?
As soon as the child thread notifies, the method called back in the main thread is the handleMessage(msg) method.
It is in handleMessage(msg) method that we retrieve the bitmap and set it to the ImageView in the UI.
private Handler messageHandler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: ImageView img = (ImageView) findViewById(R.id.imageview01); img.setImageBitmap((Bitmap)(msg.getData().getParcelable("bitmap"))); break; case 2: TextView text = (TextView) findViewById(R.id.textview01); text.setText(msg.getData().getString("text")); break; } progressDialog.dismiss(); } };
Within this method, first we check the msg.what variable to see what the type of message we're expecting is. If it is 1, which we had set in downloadImage(..) method, then, we do the required things to get a handle to the ImageView object and then give the bitmap to it.
How do we get the data from the msg object?
Through getData(). Then we use the key bitmap to retrieve the bitmap and cast it to Bitmap before setting it to the ImageView.
Finally, we dismiss the progress dialog.
Our layout file, /res/layout/main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbars="vertical"> <Button android:id="@+id/Button01" android:text="@string/button01" android:layout_height="wrap_content" android:layout_width="wrap_content"> </Button> <Button android:id="@+id/Button02" android:layout_height="wrap_content" android:text="@string/button02" android:layout_width="wrap_content"> </Button> <ImageView android:id="@+id/imageview01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:cropToPadding="true" > </ImageView> <TextView android:id="@+id/textview01" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.bogotobogo.httpconnecta"> <uses-sdk android:minSdkVersion="14" /> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".HttpURLConnectionA" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Files used in this HTTP Connection example A, HttpURLConnectionA.zip
This is another example of HTTP connection implementation using the classes HttpConnection and ConnectionManager. It uses Apache's HttpClient methods and allows for GET, POST, PUT and DELETE requests. A built in Bitmap decoder is also included.
To receive status updates from an HttpConnection a Handler is used. The HttpConnection will dispatch a message to the Handler at the occurrence of the following:
- HttpConnection.DID_START
The connection is removed from the ConnectionManager's queue and started. - HttpConnection.DID_SUCCEED
The connection was successful and the response is stored in the Message instance obj field - HttpConnection.DID_ERROR
The connection failed and the exception is stored in the Message instance obj field
This example also gets an image and text from the web. But unlike the previous example, we do not any buttons, and it will get the image and text automatically from a separate thread.
Here are the files used in this example.
HttpActivity.java:
package com.bogotobogo.httpconnectionb; import android.app.Activity; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.ImageView; import android.widget.TextView; public class HttpActivity extends Activity { private ImageView image; private TextView text; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); image = (ImageView) findViewById(R.id.image); text = (TextView) findViewById(R.id.text); downloadTwitterIcon(); downloadTwitterStream(); } public void downloadTwitterIcon() { Handler handler = new Handler() { public void handleMessage(Message message) { switch (message.what) { case HttpConnection.DID_START: { Log.d("Image", "Starting Connection"); break; } case HttpConnection.DID_SUCCEED: { Bitmap response = (Bitmap) message.obj; image.setImageBitmap(response); break; } case HttpConnection.DID_ERROR: { Exception e = (Exception) message.obj; e.printStackTrace(); break; } } } }; new HttpConnection(handler).bitmap ("http://www.bogotobogo.com/images/HTML5/HTML5-Number5.png"); } public void downloadTwitterStream() { Handler handler = new Handler() { public void handleMessage(Message message) { switch (message.what) { case HttpConnection.DID_START: { text.setText("Starting connection..."); break; } case HttpConnection.DID_SUCCEED: { String response = (String) message.obj; text.setText(response); break; } case HttpConnection.DID_ERROR: { Exception e = (Exception) message.obj; e.printStackTrace(); text.setText("Connection failed."); break; } } } }; new HttpConnection(handler).get ("http://www.bogotobogo.com/HTML5/HTML5_Tutorial.php"); } }
HttpConnection.java:
package com.bogotobogo.httpconnectionb; import java.io.*; import org.apache.http.*; import org.apache.http.client.*; import org.apache.http.client.methods.*; import org.apache.http.entity.*; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.HttpConnectionParams; import android.graphics.*; import android.os.*; public class HttpConnection implements Runnable { public static final int DID_START = 0; public static final int DID_ERROR = 1; public static final int DID_SUCCEED = 2; private static final int GET = 0; private static final int POST = 1; private static final int PUT = 2; private static final int DELETE = 3; private static final int BITMAP = 4; private String url; private int method; private Handler handler; private String data; private HttpClient httpClient; public HttpConnection() { this(new Handler()); } public HttpConnection(Handler h) { handler = h; } public void create(int method, String url, String data) { this.method = method; this.url = url; this.data = data; ConnectionManager.getInstance().push(this); } public void get(String url) { create(GET, url, null); } public void post(String url, String data) { create(POST, url, data); } public void put(String url, String data) { create(PUT, url, data); } public void delete(String url) { create(DELETE, url, null); } public void bitmap(String url) { create(BITMAP, url, null); } public void run() { handler.sendMessage(Message.obtain(handler, HttpConnection.DID_START)); httpClient = new DefaultHttpClient(); HttpConnectionParams.setSoTimeout(httpClient.getParams(), 25000); try { HttpResponse response = null; switch (method) { case GET: response = httpClient.execute(new HttpGet(url)); break; case POST: HttpPost httpPost = new HttpPost(url); httpPost.setEntity(new StringEntity(data)); response = httpClient.execute(httpPost); break; case PUT: HttpPut httpPut = new HttpPut(url); httpPut.setEntity(new StringEntity(data)); response = httpClient.execute(httpPut); break; case DELETE: response = httpClient.execute(new HttpDelete(url)); break; case BITMAP: response = httpClient.execute(new HttpGet(url)); processBitmapEntity(response.getEntity()); break; } if (method < BITMAP) processEntity(response.getEntity()); } catch (Exception e) { handler.sendMessage(Message.obtain(handler, HttpConnection.DID_ERROR, e)); } ConnectionManager.getInstance().didComplete(this); } private void processEntity(HttpEntity entity) throws IllegalStateException, IOException { BufferedReader br = new BufferedReader(new InputStreamReader(entity .getContent())); String line, result = ""; while ((line = br.readLine()) != null) result += line; Message message = Message.obtain(handler, DID_SUCCEED, result); handler.sendMessage(message); } private void processBitmapEntity(HttpEntity entity) throws IOException { BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity); Bitmap bm = BitmapFactory.decodeStream(bufHttpEntity.getContent()); handler.sendMessage(Message.obtain(handler, DID_SUCCEED, bm)); } }
ConnectionManager.java:
package com.bogotobogo.httpconnectionb; import java.util.ArrayList; public class ConnectionManager { public static final int MAX_CONNECTIONS = 5; private ArrayListactive = new ArrayList (); private ArrayList queue = new ArrayList (); private static ConnectionManager instance; public static ConnectionManager getInstance() { if (instance == null) instance = new ConnectionManager(); return instance; } public void push(Runnable runnable) { queue.add(runnable); if (active.size() < MAX_CONNECTIONS) startNext(); } private void startNext() { if (!queue.isEmpty()) { Runnable next = queue.get(0); queue.remove(0); active.add(next); Thread thread = new Thread(next); thread.start(); } } public void didComplete(Runnable runnable) { active.remove(runnable); startNext(); } }
Files used in this HTTP Connection example B, HttpURLConnectionB.zip
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization