我正在执行一个Download Manager项目,因此,为了显示所有已下载的/下载操作,我更喜欢使用ListView来显示我的下载列表。假设我们有尽可能多的下载任务,因此必须更新所有任务的进度条。对于后台下载任务,我创建了一个名为HttpDownloader的新类。因此,我将这些进度条传递给此类对象。当一个新对象添加到我的任务列表中时,我将调用HttpDownloader的构造函数并将新项目进度栏传递给它。让我感到困惑的是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.所以,我的问题是,

1.调用notifyDataSetChanged之后,对旧listview对象的引用将被破坏。

2.如果是,我如何保留旧视图的引用?

3.如果否,请向我解释,为什么后台进程强制通过进度栏以更改进度值时,进度栏重置为默认值并且不更改?

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;
    }
}

最佳答案

好吧,如果您想保留引用以显示一个好的下载管理器,那么请按部分查看操作方法。

这个概念很容易理解,我们有线程下载项目,我们将调用每个工作任务。每个任务都与列表视图中的一行相关联,因为您要显示每个项目下载的下载进度。

因此,如果每个任务与每一行都相关联,那么我们将保存实际上正在执行的任务,这样,我们就始终知道正在运行的任务及其状态。这一点很重要,因为正如我之前说过的,getView()方法被多次调用,并且我们需要知道每次下载的过程及其状态。

让我们看一下适配器的一些代码:

class MyAdapter extends ArrayAdapter<Uri> {
    ArrayList<Uri> M_list;
    Map<Uri, HttpDownloader> taskMap;

    public MyAdapter(ArrayList<Uri> list) {
        super(MainActivity.this, R.layout.download_item, list);
        this.M_list = list;

        taskMap = new HashMap<Uri, HttpDownloader>();
    }

    @Override
    public int getCount() {
        return this.M_list.size();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.download_item, parent, false);

        ProgressBar progressBar = (ProgressBar) rowView.findViewById(R.id.progressBar);

        configureRowWithTask(position, progressBar);

        return rowView;
    }

    private void configureRowWithTask(int position,final ProgressBar bar) {
        Uri url = M_list.get(position);
        HttpDownloader task;

        if (!(taskMapHasTaskWithUrl(url))) {
            task = new HttpDownloader(url, bar);
            Thread thread = new Thread(task);
            thread.start();
            taskMap.put(url, task);
        } else {
            task = taskMap.get(url);
            bar.setProgress(task.getProgress());
            task.setProgressBar(bar);
        }
    }

    private boolean taskMapHasTaskWithUrl(Uri url) {
        if (url != null) {
            Iterator taskMapIterator = taskMap.entrySet().iterator();

            while(taskMapIterator.hasNext()) {
                Map.Entry<Uri, HttpDownloader> entry = (Map.Entry<Uri, HttpDownloader>) taskMapIterator.next();
                if (entry.getKey().toString().equalsIgnoreCase(url.toString())) {
                    return true;
                }
            }
        }

        return false;
    }
}


关于它的一些解释:


我已将您的适配器类型更改为Uris的ArrayAdapter,以更好地使用链接。
我添加了一个映射,该映射具有键-值对,键是Uris和值是HttpDownloaders。该图将保存正在使用的每个任务。
我添加了两个重要的方法,一个是configureRowWithTask(),就像它自己的名字一样,它与任务一起配置一行,并将它们关联起来,但是仅当该任务是新任务时才使用,在其他情况下则是一个正在使用的任务。
另一个方法是taskMapHasTaskWithUrl(),仅检查给定任务(通过其url)当前是否在taskMap中。
可能最重要的方法是configureRowWithTask(),我将对其进行深入解释。


configureRowWithTask()方法检查是否正在使用一个任务,因为多次调用getView()都需要此方法,但是我们不希望同一下载任务被执行多次,因此该方法检查是否是新任务(在taskMap中不存在)然后创建HttpDownloader的新实例,并将新任务放入地图中。

如果taskMap中存在请求的任务,则意味着该任务先前已被请求,但是列表中的行已消失,并且已经有一个对getView()方法的新调用。因此,该任务已存在,我们不必再次创建它,可能是该任务正在下载项目,而我们唯一要做的就是查看其下载进度并将其设置到该行的进度栏中。最后将引用设置为进度条,可能是HttpDownloader丢失了该引用。

清楚这一部分之后,我们来看第二部分HttpDownloader类,让我们看一些代码:

public class HttpDownloader implements Runnable {

private Uri url;

private ProgressBar progressBar;
private int progress = 0;

private Handler handler;

public HttpDownloader (Uri url, ProgressBar progressBar) {
    this.url = url;
    this.progressBar = progressBar;
    progressBar.setProgress(progress);

    handler = new Handler();
}

@Override
public void run() {
    DefaultHttpClient client = new DefaultHttpClient();
    HttpGet get = new HttpGet(url.toString());
    HttpResponse response;


    try {
        response = client.execute(get);
        InputStream is = response.getEntity().getContent();
        long contentLength = response.getEntity().getContentLength();
        int downloadedLen = 0;
        int readBytes = 0;

        byte [] buffer = new byte [1024];


        while ((readBytes = is.read(buffer, 0, buffer.length)) != -1) {
            downloadedLen += readBytes;

            //Some storing to file codes

            progress = (int) ((100f * downloadedLen) / contentLength);

            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (progressBar != null) {
                        progressBar.setProgress(progress);
                    }
                }
            });
        }

    } catch (ClientProtocolException e) {
        Log.e("HttpDownloader", "Error while getting response");
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("HttpDownloader", "Error while reading stream");
    }
}

public int getProgress() {
    return progress;
}

public void setProgressBar(ProgressBar progressBar) {
    this.progressBar = progressBar;
}
}


这个类基本上是相同的,不同之处在于我使用Handler来更改UI线程中进度条的进度,并且仅在对进度条的引用不为null时才更改此进度。

重要的是每次下载字节时,都会刷新下载进度的值。

我已经在测试应用程序中检查了代码,似乎一切正常,如果您遇到一些问题,请告诉我,我会尽力帮助您; )

希望你能理解。

10-07 23:06