在自定义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
相关产品推荐
相关产品推荐

