如何将我的图像下载程序移动到Android上的单独线程中?

时间:2010-04-05 12:49:46

标签: android multithreading imageview android-arrayadapter

我的Android设备上运行了以下代码。它运行良好,并显示我的列表项奇妙。它也很聪明,它只在ArrayAdapter需要时才下载数据。但是,当缩略图的下载正在进行时,整个列表会停止,并且在完成下载之前无法滚动。有什么方法可以解决这个问题,所以它仍然会愉快地滚动,也许会显示下载图像的占位符,完成下载,然后显示?

我想我只需要将我的downloadImage类放入它自己的线程中,以便它与UI分开执行。但是如何将它添加到我的代码中是个谜!

对此的任何帮助都将非常感激。

private class CatalogAdapter extends ArrayAdapter<SingleQueueResult> {

    private ArrayList<SingleQueueResult> items;

    //Must research what this actually does!
    public CatalogAdapter(Context context, int textViewResourceId, ArrayList<SingleQueueResult> items) {
        super(context, textViewResourceId, items);
        this.items = items;

    }

    /** This overrides the getview of the ArrayAdapter. It should send back our new custom rows for the list */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.mylists_rows, null);

        }
        final SingleQueueResult result = items.get(position);
        // Sets the text inside the rows as they are scrolled by!
        if (result != null) {

            TextView title = (TextView)v.findViewById(R.id.mylist_title);
            TextView format = (TextView)v.findViewById(R.id.mylist_format);
            title.setText(result.getTitle());
            format.setText(result.getThumbnail());

            // Download Images
            ImageView myImageView = (ImageView)v.findViewById(R.id.mylist_thumbnail);
            downloadImage(result.getThumbnail(), myImageView);

        }

        return v;
    }
}

// This should run in a seperate thread
public void downloadImage(String imageUrl, ImageView myImageView) {

    try {
        url = new URL(imageUrl);
        URLConnection conn = url.openConnection();
        conn.connect();
        InputStream is = conn.getInputStream();
        BufferedInputStream bis = new BufferedInputStream(is);
        Bitmap bm = BitmapFactory.decodeStream(bis);
        bis.close();
        is.close();
        myImageView.setImageBitmap(bm);
    } catch (IOException e) {
        /* Reset to Default image on any error. */
        //this.myImageView.setImageDrawable(getResources().getDrawable(R.drawable.default));
    }
}

3 个答案:

答案 0 :(得分:4)

以下是我在我的应用中使用的简化版本:

// this lives in Application subclass and is reused
public static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();

// the method itself

    public void attachImage(final String fileUrl, final ImageView view) {
        EXECUTOR.execute(new Runnable() {

            @Override
            public void run() {
                final Drawable image = methodThatGetsTheImage(fileUrl);
                if (image != null) {
                    view.post(new Runnable() {

                        @Override
                        public void run() {
                            view.setImageDrawable(drawable);
                        }
                    });
                }
            }
        });
    }

答案 1 :(得分:1)

您还可以查看cwac-thumbnail以获取插入式解决方案(尽管它比上面的简单解决方案有更多的开销)。我几乎在任何可以下载网页图片的地方使用它都非常成功;你基本上为你的应用程序设置了一些全局缓存选项,然后将ListAdapter包装在ThumbnailAdapter中,列表中列出了image:ids的image:ids,并在适配器的getView方法中的imageviews上调用setTag(imageUrl),它处理剩下的事情。

答案 2 :(得分:0)

是。您应该尽可能避免在UI线程上运行。当你在getView()中时,你在UI线程上。要突破这样做:

new Thread(new Runnable(){

  public void run() {
    // your code here

  }}).start();

然后,当您需要以某种方式修改视图时,请返回到这样的UI线程:

runOnUiThread(new Runnable(){

  public void run() {
    // your view-modifying code here

  }});

..或使用View.post()机制。

在您的示例中,将会发生的事情是,在图像可供显示之前,该项目将显示在屏幕上。我建议使用'loading'微调器或其他占位符来向用户指示发生了什么。

您可以在实践中多次这样嵌套,尽管您将达到简化设计的顺序。匿名类可能在内存方面效率低下,并且在没有取消机制的情况下触发大量线程可能有其自身的缺点。

在getView()退出后修改视图的副作用可能会遇到一些困难。某些视图可能需要使用invalidate()来使其更改生效。还要注意,如果要在getView()中回收视图,则应确保在执行视图更改代码时视图仍在用于您正在应用的内容。 setTag()/ getTag()在此处用于存储您稍后可以检查的一些数据ID,以确保您的View未被回收。