You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在自定义Gallery的ImageAdapter中用AsyncTask加载图片解决UI线程过载

解决Gallery的ImageAdapter UI线程过载问题(用AsyncTask异步加载图片)

我来帮你搞定这个UI线程忙不过来的问题!你提到在自定义Gallery的ImageAdapter时收到了"UI线程工作过多"的提示,想要用AsyncTask来异步加载图片,尤其是针对GifView的场景对吧?下面结合你已有的代码,给你整理完整的实现方案:

完整的ImageAdapter + AsyncTask实现代码

public class ImageAdapter extends BaseAdapter {
    private ArrayList<Media> listeImages;
    private Context mContext;
    // 图片缓存,避免重复加载同一图片
    private LruCache<String, Bitmap> imageCache;

    public ImageAdapter(ArrayList<Media> liste, Context context) {
        this.listeImages = liste;
        this.mContext = context;
        // 初始化LruCache:用应用可用内存的1/8作为缓存上限
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        imageCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }

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

    @Override
    public Object getItem(int position) {
        return listeImages.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        Media currentMedia = listeImages.get(position);

        // 复用convertView,减少View创建开销
        if (convertView == null) {
            // 根据媒体类型加载对应布局(Gif用GifView,普通图用ImageView)
            if (currentMedia.isGif()) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.item_gif, parent, false);
                holder = new ViewHolder();
                holder.gifView = convertView.findViewById(R.id.gif_view);
            } else {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.item_image, parent, false);
                holder = new ViewHolder();
                holder.imageView = convertView.findViewById(R.id.image_view);
            }
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // 异步加载媒体资源
        if (currentMedia.isGif()) {
            new LoadGifTask(holder.gifView, mContext).execute(currentMedia.getPath());
        } else {
            new LoadImageTask(holder.imageView, imageCache).execute(currentMedia.getPath());
        }

        return convertView;
    }

    // ViewHolder模式:避免重复调用findViewById
    static class ViewHolder {
        ImageView imageView;
        GifView gifView;
    }

    // 加载普通图片的AsyncTask
    static class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
        // 用弱引用防止内存泄漏
        private final WeakReference<ImageView> imageViewRef;
        private final LruCache<String, Bitmap> cacheRef;
        private String imagePath;

        public LoadImageTask(ImageView imageView, LruCache<String, Bitmap> cache) {
            imageViewRef = new WeakReference<>(imageView);
            cacheRef = cache;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            imagePath = params[0];
            // 先查缓存,有就直接返回
            Bitmap cachedBitmap = cacheRef.get(imagePath);
            if (cachedBitmap != null) {
                return cachedBitmap;
            }
            // 压缩图片再加载,避免OOM
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imagePath, options);
            // 计算采样率,适配控件大小(这里假设控件是200x200)
            options.inSampleSize = calculateInSampleSize(options, 200, 200);
            options.inJustDecodeBounds = false;
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
            // 存入缓存
            if (bitmap != null) {
                cacheRef.put(imagePath, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            ImageView imageView = imageViewRef.get();
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
        }

        // 计算图片采样率的工具方法
        private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;

            if (height > reqHeight || width > reqWidth) {
                final int halfHeight = height / 2;
                final int halfWidth = width / 2;
                while ((halfHeight / inSampleSize) >= reqHeight
                        && (halfWidth / inSampleSize) >= reqWidth) {
                    inSampleSize *= 2;
                }
            }
            return inSampleSize;
        }
    }

    // 加载Gif的AsyncTask
    static class LoadGifTask extends AsyncTask<String, Void, InputStream> {
        // 弱引用防止内存泄漏
        private final WeakReference<GifView> gifViewRef;
        private final WeakReference<Context> contextRef;
        private String gifPath;

        public LoadGifTask(GifView gifView, Context context) {
            gifViewRef = new WeakReference<>(gifView);
            contextRef = new WeakReference<>(context);
        }

        @Override
        protected InputStream doInBackground(String... params) {
            gifPath = params[0];
            try {
                return new FileInputStream(gifPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(InputStream inputStream) {
            GifView gifView = gifViewRef.get();
            Context context = contextRef.get();
            if (gifView != null && inputStream != null && context != null) {
                // 这里根据你用的GifView库调整加载逻辑,有些库直接支持setFilePath
                gifView.setGifStream(inputStream);
                // 或者:gifView.setGifPath(gifPath);
            }
        }
    }
}

关键细节说明

  • ViewHolder模式:必须用上,大幅减少findViewById的调用次数,提升列表滑动流畅度。
  • 弱引用WeakReference:在AsyncTask中持有控件和Context的弱引用,避免因AsyncTask未完成导致的内存泄漏。
  • LruCache缓存:避免重复加载同一图片,减少IO操作和内存消耗,同时防止OOM。
  • 图片压缩:加载普通图片时计算采样率,把图片压缩到适配控件的大小再加载,避免大图片占用过多内存。
  • Gif加载适配:根据你实际使用的GifView库调整加载逻辑,部分库直接支持传入文件路径,可简化代码。

额外优化建议

如果你的图片数量较多,AsyncTask的默认线程池可能出现过载问题,建议改用Executors.newFixedThreadPool()控制并发数;或者直接使用Glide、Picasso等成熟图片加载库,它们已经帮你封装好了异步加载、缓存、内存管理等所有细节,比自己写AsyncTask更稳定高效。

内容的提问来源于stack exchange,提问作者cilies38

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:39:55