1

在 Android 应用程序中,我有一个图像 URL 列表,如下所示:

List<String> urls = new ArrayList<String>(100);
urls.add("http://www.example.org/1.jpg");
urls.add("http://www.example.org/2.png");
urls.add("http://www.example.org/3.jpg");
urls.add("http://www.example.org/4.jpg");
urls.add("http://www.example.org/5.png");
urls.add("http://www.example.org/6.png");
urls.add("http://www.example.org/7.png");
urls.add("http://www.example.org/8.jpg");
urls.add("http://www.example.org/9.jpg");
urls.add("http://www.example.org/10.gif");
    ...
urls.add("http://www.example.org/100.jpg");

现在我必须获取所有这些 URL 的文件大小和 MIME 类型,当然,这应该尽可能快地完成。

我所做的是以下内容:

for (String url : urls) {
    int fileSize;
    try {
        URLConnection urlConnection;
        urlConnection = new URL(url).openConnection();
        urlConnection.connect();
        final String mimeType = urlConnection.getContentType();
        final int fileSize = urlConnection.getContentLength();
        // do something with those two pieces of information
    }
    catch (MalformedURLException e) {
        continue;
    }
    catch (IOException e) {
        // some special handling
    }
}

但这非常慢。这是因为它使用单个线程并一个一个地请求 URL,而 Web 浏览器总是一次访问多个文件,不是吗?

那么我怎样才能让它更快呢?

对于HttpClient,我读到您应该重用实例,并且有一些方法可以在多线程环境中使用它们。

但是我将如何使用URLConnection或任何其他为您提供文件大小和 MIME 类型的类来做到这一点?

编辑:

这些图像并不都在同一个主机上,而是分布在几个服务器上,比如分布在 5 个主机名上的 100 个图像。

您可以使用几个线程或一次运行多个 AsynTask 来完成这项工作吗?有没有什么需要注意的,比如回收URLConnection物品之类的?

我不太确定如何使用多个线程来共享任务列表(100 个图像文件)并在之后合并结果(MIME 类型和文件大小)。你能帮我吗?

4

3 回答 3

3

把你的工作分成更小的部分,让工人Thread来处理:

工人Thread

public class Downloader extends Thread {

    private final String mUrl;
    private String mMimeType;
    private int mFileSize;

    public Downloader(String url) {
        mUrl = url;
    }

    @Override
    public void run() {
        try {
            URLConnection urlConnection;
            urlConnection = new URL(mUrl).openConnection();
            urlConnection.connect();
            mMimeType = urlConnection.getContentType();
            mFileSize = urlConnection.getContentLength();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getMimeType() {
        return mMimeType;
    }

    public int getFileSize() {
        return mFileSize;
    }
}

实例化、运行并等待工作者:

ArrayList<String> urls = new ArrayList<String>(10);
// ...
ArrayList<Thread> threads = new ArrayList<Thread>(10);
for (String url : urls) {
    Thread t = new Downloader(url);
    threads.add(t);
    t.start();
}
for (Thread t : threads) {
    try {
        // do not wait for other threads  in main UI thread!
        t.join();
        //((Downloader) t).getMimeType();
        //((Downloader) t).getFileSize();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Thread请务必注意在您的 UI 中等待工作人员Thread

上面的答案只能用于一小组 URL。AThreadPool可能不是必需的,因为Threads 大部分时间都会等待 IO。


这是您要求的答案,带有ThreadPool.

它使用与Downloader上面示例相同的类,只有一个更改:生成Threads 由 the 完成,ThreadPool单个任务不再需要是真实Thread的。所以让Downloader实现 aRunnable而不是扩展 a Thread

public class Downloader implements Runnable {

希望这是您正在寻找的。

public class ThreadedDownloader {

    private static final int KEEP_ALIVE_TIME = 1;
    private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
    private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
    private LinkedBlockingQueue<Runnable> mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
    private ThreadPoolExecutor mDecodeThreadPool = new ThreadPoolExecutor(NUMBER_OF_CORES,
            NUMBER_OF_CORES, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, mDecodeWorkQueue) {
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            Downloader d = (Downloader) r;
            // do something with finished Downloader d
            // like saving it's result to some sort of list
            // d.getMimeType();
            // d.getFileSize();

            if (mDecodeWorkQueue.isEmpty()) {
                onAllDownloadsFinised();
            }
        }
    };

    /** Download a list of urls and check it's mime time and file size. */
    public void download(List<String> urls) {
        for (String url : urls) {
            mDecodeThreadPool.execute(new Downloader(url));
        }
    }

    /** Calles when all downloads have finished. */
    private void onAllDownloadsFinised() {
        // do whatever you want here
        // update UI or something
    }
}
于 2013-09-12T04:16:19.277 回答
2

我没有示例代码,但HTTP 动词 HEAD 是您正在寻找的。它在不传输内容主体的情况下检索标题(包括 mime 和内容长度)。

这个答案更详细地说明了 HEAD 的作用。

于 2013-09-20T21:58:35.353 回答
1

我认为您在已经提交的答案中拥有大部分(如果不是全部)解决方案。

这是我要做的:

1) 使用 Executors.newCachedThreadPool() 发出头部请求;

2)将响应存储在地图中

3)创建有效载荷映射

4) 使用 AsyncTask 实例加载真实图像

5)当每个位图加载完成后,将结果存入payload map

只是我的想法。

于 2013-09-21T05:08:56.810 回答