{Loading Images from Remote Server over HTTP on a Separate Thread}
by Binil Thomas
Whenever a user launches an Android application, a thread called “main” is automatically created. This main thread, also called the UI thread, is crucial since it’s responsible for dispatching events to various widgets, including the drawing events. For careless Android developers, this single thread can be the overarching reason for poor app performance. With so much riding on a single thread performing long operations like network access or database queries, any interference on this thread will block up the entire user interface. No events can be dispatched – including drawing events – while long operations are underway, and from the user’s perspective, the application appears hung. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user will encounter the dreaded “application not responding” (ANR) dialog.
BaseAdapter is an Adapter object that acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to specific data points and is also responsible for making a View for each point in the data set. So, taking the example of downloading an image over HTTP, there may be some slowdown in our application with the Main UI thread getting blocked for more than a few seconds, and users might be presented with the ANR dialog. To avoid this situation I have some suggestions:
1. Use ExecuterService (Thread pool)
2. Use AsynTask
3. Use SoftReference Drawable Object
4. Create the ListView first, then download images
5. Make use of OnScrollListener Interface
I’ve written a sample application to help Android developers visualize this implementation.
Here we have AbastractListAdapter, an extension of BaseAdapterClass:
public abstract class AbstactListAdapter extends BaseAdapter {
public AbstactListAdapter(Context context) {
this.context = context;
}
public abstract void setScrollStatus(boolean scroll);
public abstract void scrollIdle(AbsListView view);
}
There are two methods of implementation for Class ListAdapterScrollListener and Onscrollistner.
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
adapter.setScrollStatus(true);
}
public void onScrollStateChanged(AbsListView view,intSrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.scrollIdle(view);
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setScrollStatus(true);
break;
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setScrollStatus(true);
break;
}
}
In my onCreate method I set up and configure the list view to load the views and set the listner for Scroll.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Adapter mAdapter=new Adapter(this);
setListAdapter(mAdapter);
getListView().setOnScrollListener(new ListAdapterScrollListener(mAdapter));
}
I use an Adapter class to extend AbstractAdapterClass and implement BaseAdapter, both of which help to load images when scrolling is idle.
The image is loaded in a separate threadpool. I have used Executerservice and assign a threadpool to download images. This method employs a runnable which will extend Assigntask. This ImageLoader class has a subclass, workerthread, which extends Assyntask and implements runnables. This class uses ExecutorService, and assigns 5 threads to do the work. Other implementations of this kind launch a separate thread per image, meaning that the network connection will become clogged with any image load. Assigning the task to the workerthread subclass and using onprogressupdate to notify the adapter to display the image reduces the burden.
You’ll notice the use of SoftReference here. While this appears to work well, I haven’t done any significant load or performance testing, so it may not be necessary. Since bitmap/drawable objects require memory, and Android supports 16mb of heap, it’s better to classify those objects as SoftReference objects. SoftReferences pointing to softly reachable objects are guaranteed to be cleared before the VM triggers an OutOfMemoryError. This can be written as follows:
public class ImageLoader {
private class WorkerThread extends AsyncTask implements Runnable{
public WorkerThread(String url,int pos) {
this.url=url;
intex=pos;
}
@Override
protected void onProgressUpdate(Void... values) {
mAdapter.notifyDataSetChanged();
super.onProgressUpdate(values);
}
@Override
public void run() {
Cache.put(intex,new SoftReference(readDrawableFromNetwork(this.url)));
publishProgress();
}
}
public Drawable getDrawble(int pos)
{
SoftReference mReference=Cache.get(pos);;
if(mReference!=null)
return mReference.get();
else
return null;
}
/*method to get Image if already or to start new task to download*/
public void loadImage(int First,int Last) {
try{
_exec = Executors.newFixedThreadPool(5);
for(int pos=First;pos<=Last;pos++){
if(Cache.containsKey(pos)){
if(Cache.get(pos).get()==null)
_exec.execute(new WorkerThread(Dataset.mStrings[pos],pos));
}else{
_exec.execute(new WorkerThread(Dataset.mStrings[pos],pos));
}
}
}catch (Exception e) {
_exec.shutdown();
}
}//end of method
private static Drawable readDrawableFromNetwork(String url ) {
Write code here to downlaod image from network.
}//end of method
}
And that’s it!
For your reference, the code has been packaged for you to download and inspect here.

Comments
No comments yet.
Add Comment
You must be logged in to post a comment.