【问题标题】:Loading Bitmaps with Async Task in ListView Adapter在 ListView 适配器中使用异步任务加载位图
【发布时间】:2015-07-09 05:40:13
【问题描述】:

我在尝试管理 AsyncTask 类中的位图时遇到问题。

我认为我的一切设置都很好,但是当我尝试显示代码在循环中执行的图像时会发生什么,并且相同的图像视图最终会出现故障,一次在同一图像中设置许多不同的图像(它是很难解释)。我正在通过适配器中的 getView 方法执行 AsyncTask。

这是我的 ListView 适配器类,也许有人可以弄清楚它有什么问题:

    public class SongsAdapterNew extends ArrayAdapter<String> {

    ArrayList<String> names;
    ArrayList<String> images;
    Activity context;
    ImageView imageView;

    static class ViewHolder {

        private TextView text;
        private ImageView image;
    }

    public SongsAdapterNew(Activity context, ArrayList<String> names,
            ArrayList<String> images) {
        super(context, R.layout.songs_row, names);

        this.names = names;
        this.context = context;
        this.images = images;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub

        ViewHolder mViewHolder = null;

        if (convertView == null) {
            mViewHolder = new ViewHolder();
            LayoutInflater vi = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = vi.inflate(R.layout.songs_row, parent, false);
            mViewHolder.text = (TextView) convertView
                    .findViewById(R.id.songTextView);
            imageView = mViewHolder.image = (ImageView) convertView
                    .findViewById(R.id.imageView1);
            convertView.setTag(mViewHolder);

        }

        else {

            mViewHolder = (ViewHolder) convertView.getTag();

        }

        mViewHolder.text.setText(names.get(position));

        new ImageLoader().execute(mViewHolder.image, images.get(position));

        return convertView;
    }

    public class ImageLoader extends AsyncTask<Object, String, Bitmap> {

        private View view;
        private Bitmap bitmap = null;
        MediaMetadataRetriever mmr;
        byte[] rawArt = null;
        Bitmap art1;
        BitmapFactory.Options bfo;

        @Override
        protected Bitmap doInBackground(Object... parameters) {

            // Get the passed arguments here
            view = (View) parameters[0];
            String uri = (String) parameters[1];

            mmr = new MediaMetadataRetriever();

            bfo = new BitmapFactory.Options();
            bfo.inSampleSize = 5;
            try {
                mmr.setDataSource(uri);
                rawArt = mmr.getEmbeddedPicture();
                bitmap = BitmapFactory.decodeByteArray(rawArt, 0,
                        rawArt.length, bfo);
            } catch (Exception e) {
            }


            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {

                imageView.setImageBitmap(bitmap);
            }

            else {

                imageView.setImageResource(R.drawable.songs);
            }

        }
    }

}

【问题讨论】:

  • 您没有考虑行回收。随着用户滚动,行被回收,您开始新任务以加载新图像......但旧任务仍然未完成。另外,使用execute(),旧任务将在新任务开始之前运行完成。一般来说,这个问题已经被许多图像加载库(例如 Picasso、Universal Image Loader)解决了。哪些支持您的特定数据源,我不能说,尽管有些应该有可插入的方式来规定要加载的图像。
  • 那些图像加载库不支持我的数据源,我传递 AsyncTask 的字符串是用户设备上特定歌曲的直接路径,而不是艺术。
  • “那些图像加载库不支持我的数据源”——是的,但您可以插入自己的数据源。在 Universal Image Loader 的情况下,制作你自己的方案(例如,media.metadata://),扩展BaseImageDownloader,覆盖getStreamFromOtherSource(),把你的MediaMetadataRetriever逻辑放在那里,然后通过imageDownloader()在@987654328上注册你的下载器@。然后,以您的方案开头的路径将路由到您的 getStreamFromOtherSource(),而 UIL 将处理所有异步内容、缓存、行回收等。
  • CommonsWare 所说的。尤其是第一句话。如果您不想使用库(但我不明白为什么不应该使用),请在回收视图时取消图像。
  • @Christine 如果可以的话,我很想使用图像加载库,但我不会从 URLS 加载图像。我会尝试尝试 CommonsWare 的解决方案。

标签: android android-listview android-adapter


【解决方案1】:

我使用了与您类似的东西,避免使用像 Picasso 或 Universal UIL 这样的大型库,有一天我可能会获得更快的性能。但是使用 AsyncTask 有点棘手,但 Google 已经很好地记录了它。

无论如何我认为你的问题是。

更改自:

imageView.setImageBitmap(bitmap);

到:

view.setImageBitmap(bitmap);

注意:您不应使用imageView 的更广泛的类范围。相反,您需要引用 view 对象来访问行项目中的特定图像

我认为这可以解释您的说法“一次在同一图像中设置许多不同的图像......”。

【讨论】:

  • @Jack,你试过这个简单的修复方法吗?对于你这个阶段的发展,我认为这将解决你当前的问题。
【解决方案2】:

如果您真的想远离 3rd 方库,您需要在模型中保留 Bitmap 数据。由于视图被回收,视图(和持有者)将被交换。

这是一个粗略的决定,不使用位图缓存,只是具体细节。当Bitmap 准备好时,它不会通知视图。

简单的方法是创建一个包含 url 和位图的 Image 类

public final class Image {
    public final String url;
    private Bitmap bitmap;
    private WeakReference<AsyncTask> imageLoaderWeakReference;

    public Image(String url) {
        this.url = url != null ? url : url;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void load() {
        imageLoader = new ImageLoader() {
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                ...
                this.bitmap = bitmap;
            }
        };
        imageLoader.execute(mViewHolder.image, url);
        imageLoaderWeakReference = new WeakReference<>(imageLoader);
    }
}

然后在您的适配器构造函数中传递Image 实例而不是字符串的列表 - images

最后在你的getView() 中更新new ImageLoader().execute(mViewHolder.image, images.get(position));

Image image = images.get(position);
Bitmap bitmap = image.getBitmap();
if (bitmap != null) {
    // set your image view with the bitmap
}
else {
    image.load();
}

最终你可以在Image 中添加一个ImageView 弱引用,但这需要提前考虑。

【讨论】:

    【解决方案3】:

    正如@TheOriginalAndroid 所说,您看到的错误的来源可能是在 imageView 变量(具有整个适配器的范围)上设置位图,而不是在视图上(顺便说一句,在AsyncTask 的范围内并保持传递的引用在列表中正确ImageView)。

    但是,在AsyncTask 中,出于几个原因,您需要WeakReference 来查看该图像视图。首先,它不会阻止 Bitmap 回收,其次它可以防止在 ImageView 因某种原因消失但您的 AsyncTask 中的 AsyncTask 尝试在其上设置位图(不再存在因此 null ImageView)。

    查看this blog 的帖子或this video,伙计们对其进行了解释并在那里演示得很好;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多