There is often a scenario where you need to download files from a server onto the Android device. This needs to be done without forcing the user to launch a web browser and have interaction as some of the downloaded data might be private. Let’s use the scenario where you’ve made a game and you have in-app purchases. You want to keep the size of the game small for everyone so you decide to put the purchase items on your server. When a user purchases an item then the item will be downloaded in the background. This is a perfect usage example.
For purposes of the tutorial we will be staying simple. We will be downloading a remote image in the background and then displaying it when it has finished downloading.
To download remote files without a browser in Android you must use threads or AsyncTasks. This is because Android cannot perform network requests on the UI thread and in reality why would you? Downloading a large file can take time and you wouldn’t want the users device frozen during that period.
The following example was taken directly from my open source project Spyfi.
import java.io.*;
import java.net.*;
public class RemoteImage {
public RemoteImage() { }
public InputStream download(String url) {
URL snapshotUri = null;
InputStream inputStream = null;
try {
snapshotUri = new URL(url);
inputStream = snapshotUri.openStream();
} catch (Exception e) { }
return inputStream;
}
}
You’ll notice the above class will be responsible for making a server request and storing the response. Although we are expecting an image response in this example, the class can receive any kind of response, whether it be audio, database data, etc. The download method will just return a Java InputStream, so it is up to the caller to determine what happens next. In our case, the stream will be converted into a Bitmap image and then loaded into our ImageView.
import android.app.*;
import android.os.*;
import android.graphics.*;
import android.widget.*;
import java.io.*;
public class TestActivity extends Activity {
private Bitmap snapshot;
private ImageView remoteImageView;
private RemoteImage remoteImage;
private InputStream inputStream;
private Handler threadHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
remoteImageView = (ImageView) findViewById(R.id.remoteimage);
remoteImage = new RemoteImage();
threadHandler = new Handler();
new Thread(new Runnable() {
public void run() {
inputStream = remoteImage.download("http://placehold.it/100x100");
if(inputStream != null) {
snapshot = BitmapFactory.decodeStream(inputStream);
}
threadHandler.post(updateUI);
}
}).start();
}
final Runnable updateUI = new Runnable() {
public void run() {
if(snapshot != null) {
remoteImageView.setImageBitmap(snapshot);
}
}
};
}
As you can see above, there is a little more to it than just calling the download method of our class. We must do all of our requests in a separate thread to prevent system hang. The logic for our activity will start a new thread that does all of our background work. Since UI cannot be manipulated outside the UI thread, a separate runnable must be executed when the worker thread has finished. The separate runnable will load the image into the ImageView.