异步下载数百个文件的解决方案

时间:2013-05-15 22:40:04

标签: java android asynchronous httprequest

我有一个应用程序,用户可能需要下载最多760个文件,总共大约350MB。 可以压缩这些文件,它们必须以松散文件的形式下载!

我目前正在使用Android Asynchronous Http Client下载单个文件,而AsyncTask则用于运行整个过程。

以下是处理在后台下载数百个文件的DownloadThread对象的示例:

public class DownloadThread extends AsyncTask<String,String,String> {

ArrayList<String> list;
AsyncHttpClient client;
String[] allowedContentTypes = new String[] { "audio/mpeg" };
BufferedOutputStream bos;
FileOutputStream fos;

@Override
protected String doInBackground(String... params) {
    DownloadTask task;
    for (String file : list) {
        //the "list" variable has already been populated with hundreds of strings
        task = new DownloadTask(file);
        task.execute("");
        while (!task.isdone)
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }

    return null;
}


class DownloadTask extends AsyncTask<String, String, String> {

    String character, filename;
    boolean isdone = false;

    public DownloadTask(String file) {
        //file = something like "Whale/sadwhale.mp3"
        character = file.split("/")[0];
        filename = file.split("/")[1];
    }

    @Override
    protected void onPreExecute() {
    }

    @Override
    protected void onPostExecute(String result) {
        if (!result.equals("Error")) {
                        //Do something on success
        }
        isdone = true;
    }

    @Override
    protected String doInBackground(String... str) {
        client = new AsyncHttpClient();
        client.get("http://some-site.com/sounds/" + character + "/"
                + filename, new BinaryHttpResponseHandler(
                allowedContentTypes) {
            @Override
            public void onSuccess(byte[] fileData) {
                try {
                    // Make file/folder and create stream
                    File folder = new File(Environment
                            .getExternalStorageDirectory()
                            + CharSelect.directory + character);
                    folder.mkdirs();
                    File dest = new File(folder, filename);
                    fos = new FileOutputStream(dest);
                    bos = new BufferedOutputStream(fos);
                    // Transfer data to file
                    bos.write(fileData);
                    bos.flush();
                    bos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        return "Success";
    }

}

}

DownloadThread在后​​台运行,并且还会调用数百个自己的AsyncTasks。它等待任务完成下载,然后为每次下载继续for循环。

这很有效。某些下载似乎无法正常完成或根本无法启动。在760次下载列表中,平均100次下载完成正常,我必须重新启动该过程以下载另外100次下载,直到该失败。我有一种感觉,这是由于时间问题,因为Thread.sleep(10)线似乎有点“hackish”。

当然,从另一个AsyncTasks调用数百个AsyncTask并不是最有效的方法。如何更改此代码或实施第三方解决方案以适应此任务?

2 个答案:

答案 0 :(得分:3)

试用DownloadManager API。这应该是你需要的。

答案 1 :(得分:1)

这是你需要记住的事情:

计算机资源有限;网络带宽,CPU,内存,磁盘等

一次下载1个文件与同时下载760个文件所花费的时间在逻辑上不会比同时下载更长。

但是,通过生成大量后台任务/线程,您需要进行大量的线程抖动/开销,因为每个线程需要进行上下文切换。切换时将消耗CPU带宽,而不是实际将数据移入和移出网络接口。此外,每个线程将使用它自己的内存,如果不是池的一部分,可能需要创建。

基本上,你的应用程序无法正常工作的原因几乎可以肯定是因为它在完成下载或完全利用网络之前就已经耗尽了CPU / DISK-IO /内存资源。

解决方案:找到一个库来执行此操作或使用Executor类的套件并使用有限的线程池(然后一次只下载一些)。

这里有一些很好的证据表明你不想建议你做什么:

  • Google Play更新已全部序列化
  • 亚马逊MP3文件下载程序已完全序列化
  • Linux中的默认scp客户端是序列化文件传输
  • Windows更新系列下载

拍照?抛出所有这些线程是一个问题的方法,以换取感知到的速度提升。