I am working on a Download Manager project, so, to show all downloaded / downloading actions, i prefer to use ListView to show my download list. Suppose that, we have as many as downloading task, so, the progress bars of all tasks must be updated. For background download task, i created a new class that i named it HttpDownloader. So, i pass these progress bars on objects of this class. When a new object is added to my tasklist, so, i call the constructor of HttpDownloader and pass the new item progress bar to it. The thing confused me is When i add a new object to tasklist and call notifyDataSetChanged of adapter, my list is refreshed, so all progress bar reset to default layout values but HTTPDownloader Thread is running in background successfully. So, it is my question that,
1. After calling notifyDataSetChanged, references to old listview’s objects are destructs ?
2. If yes, How can i keep old view’s reference ?
3. If no, please explain me, why progress bars reset to default and do not change when background process force to passed progressbar to change the value of progress ?
HTTPDownloader class
class HttpDownloader implements Runnable {
public class HttpDownloader (String url, ProgressBar progressBar)
{
this.M_url = url;
this.M_progressBar = progressBar;
}
@Override
public void run()
{
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(this.M_url);
HttpResponse response;
try{
response = client.execute(get);
InputStream is = response.getEntity().getContent();
long contentLength = response().getEntity().getContentLength();
long downloadedLen = 0;
int readBytes = 0;
byte [] buffer = new byte [1024];
while ((readBytes = in.read(buffer, 0, buffer.length)) != -1) {
downloadedLen += readBytes;
//Some storing to file codes
runOnUiThread(new Runnable() {
@Override
public void run() {
M_progressBar.setProgress((100f * downloadedLen) / contentLength);
}
});
}
is.close();
} catch (ClientProtocolException e) {
Log.e("HttpDownloader", "Error while getting response");
} catch (IOException e) {
Log.e("HttpDownloader", "Error while reading stream");
}
}
}
AdapterClass
class MyAdapter extends ArrayAdapter<String> {
ArrayList<String> M_list;
public MyAdapter(ArrayList<String> list) {
super(MainActivity.this, R.layout.download_item, list);
this.M_list = list;
}
@Override
public int getCount() {
return this.M_list.size();
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.download_item, parent, false);
ProgressBar bar = (ProgressBar) rowView.findViewById(R.id.progrees);
new Thread (new HttpDownloader(this.M_list.get(position), bar)).start();
return rowView;
}
}
Well if you want to keep the references to show a good download manager, lets see how do it, go by parts.
The concept is easy to understand, we have threads downloading items, we are going to call each of this piece of work tasks. Each task is associated with a row in the list view, because you want to show a download progress of each item download.
So if each task is associated with each row, we are going to save tasks actually are in execution, in this way, we know in all moment what tasks are running and their state. This is importante because, like I have said before, getView() method is called many times, and we need to know each download in progress and its state.
Lets see some code of our Adapter:
Some explanations about it:
The other method is taskMapHasTaskWithUrl(), simply checks if the given task (by its url) is currently in the taskMap.
configureRowWithTask() method checks if one task is in use, this method is needed because getView() is called many times, but we dont want the same downloading task be executed more than once, so this method checks that, if the task is new (not present in the taskMap) then creates a new instance of HttpDownloader and put the new task in the map.
If the task requested is present in the taskMap, it means that the task has been requested previously but the row in the list has gone out and there have been a new call to getView() method. So the task is present, we dont have to create it again, probably the task is downloading the item and the only thing we have to do is see its download progress and set it to the row’s progress bar. And finally sets the reference to the progress bar, probably the HttpDownloader has lost this reference.
With this part clear, we go with the second part, the HttpDownloader class, lets see some code:
This class is basically the same, the diference is that I use a Handler to change the progress of the progress bar in the UI thread, and change this progress only if the reference to the progress bar is not null.
Important thing is every time bytes are downloaded, value of download progress is refreshed.
I have checked the code in a test app and seems that all runs ok, if you have some problems, let me know and I will try to help you ; )
Hope you understand it.