使用LRUCACHE解决加载本地大量图片卡顿及OOM问题

本文介绍了如何通过LRUCache解决Android应用在加载大量本地图片时遇到的卡顿和内存溢出(OOM)问题。通过在适配器中使用LRUCache缓存图片,减少对文件的频繁读取,从而提高性能和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天测试了一下之前写的一个从本地加载图片并显示的功能,因为以前测试的时候一次性显示的图片比较少,所以就没发现有卡顿以及进入界面比较慢的问题,但是今天想看一下传入很多图片之后的界面效果,所以就加载了很多图片,传入图片之后一开始进入界面,发现出错,出错运用是因为我往Adapter里面传的参数有一项是ArrayList<Bitmap>,因为一次性传入太多的BitMap,所以越界了,导致程序崩溃,然后改成传路径,这个问题得到解决,但是又发现进入界面的时候特别慢,而且往下滑动的时候也是特别的卡顿,我在重写Adapter的时候用到了优化,但是还是特别卡顿,然后想到我每加载一张图片都是去根据路径去读文件,所以导致卡顿现象特别严重,而且进入的时候也是因为每张图片都去读一下文件,所以进入特别慢,有时候甚至出现OOM的问题。这时突然间感觉自己以前写的时候脑子估计是抽风了,竟然犯了这么弱智的问题,每次读文件是很耗时的,所以程序不崩溃才怪。因此感觉使用缓存的技术来修改自己的程序。因为这次又用到了LruCache技术,所以顺便写一下使用过程吧。
其实方法跟网上大部分讲解的代码都类似,只是把从网络下载图片部分改成从本地加载,而且因为这个程序的特殊性,所以加载的图片不能有相同路径的图片,因为所有的键值对设置我都是以图片的路径做的,所以如果路径相同的话有可能会使图片显示不出来。
public class WineListAdapter extends BaseAdapter implements AbsListView.OnScrollListener {

    // 从本地加载图片的线程集合
    private List<ImageDownloadTask> mDownloadTaskList;
    private LruCache<String, Bitmap> mLruCache;
    // 引用外部的变量
    private WeakReference<GridView> mGridView;
    private WeakReference<List<String>> urls;
    private WeakReference<List<String>> names;
    private WeakReference<Context> mContext;

    // 可见项的第一项的index
    private int mFirstVisibleIndex;

    // 可见项的个数
    private int mVisibleItemCount;

    // 是不是第一次打开Activity
    private boolean isFirstOpen = true;


    public WineListAdapter(Context context, GridView mGridView, List<String> urls, List<String> names) {
        this.mContext = new WeakReference<Context>(context);
        this.urls = new WeakReference<List<String>>(urls);
        this.names = new WeakReference<List<String>>(names);
        this.mGridView = new WeakReference<GridView>(mGridView);
        this.mGridView.get().setOnScrollListener(this);
        mDownloadTaskList = new ArrayList<>();
        initCache();
    }

    private void initCache() {
        // 获取应用的max heap size
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // Android官方教学文档推荐LruCache的size为heap size的1/8
        int cacheSize = maxMemory / 8;

        mLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                if (bitmap != null) {
                    return bitmap.getByteCount() / 1024;
                }
                return 0;
            }
        };
    }


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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext.get()).inflate(R.layout.winelist_item, null);
            viewHolder = new ViewHolder();
            viewHolder.im = (ImageView) convertView.findViewById(R.id.iv_winelist);
            viewHolder.tv = (TextView) convertView.findViewById(R.id.tv_winelist);
            convertView.setTag(viewHolder);
        } else {
            //view = convertView;
            viewHolder = (ViewHolder) convertView.getTag();
        }

        String url = urls.get().get(position);
        viewHolder.im.setTag(url);
        viewHolder.tv.setText(names.get().get(position));

        if (!viewHolder.im.getTag().equals(url)) {
            showImageView(viewHolder.im, url);
        }

        return convertView;
    }

    /**
     * 给ImageView设置Bitmap
     */
    private void showImageView(ImageView imageView, String url) {


        // 先从cache中找bitmap缓存
        Bitmap bitmap = get(url);

        if (url.equals("1")) {
            imageView.setImageBitmap(BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher));
        } else if (bitmap != null) {
            // 如果缓存命中
            imageView.setImageBitmap(bitmap);
        } else {
            // 如果cache miss
            imageView.setImageBitmap(BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher));
        }
    }

    /**
     * 将Bitmap put 到 cache中
     */
    private void put(String key, Bitmap bitmap) {

        if (get(key) == null) {
            mLruCache.put(key, bitmap);
        }
    }

    /**
     * 在Cache中查找bitmap,如果miss则返回null
     */
    private Bitmap get(String key) {
        return mLruCache.get(key);
    }

    /**
     * 从本地加载图片
     */
    private Bitmap loadBitmap(String urlStr) {
        Bitmap bitmap = null;
        File file = new File(urlStr);
        if (!file.exists()) {
            //Toast.makeText(mContext.get(), "文件损坏,请查看", Toast.LENGTH_SHORT).show();
            Log.i("---LK---", "no file");
            bitmap = BitmapFactory.decodeResource(mContext.get().getResources(), R.mipmap.ic_launcher);
        } else {
            bitmap = BitmapFactory.decodeFile(urlStr);
        }
        return bitmap;
    }

    /**
     * 加载可见项的图片
     */
    private void loadVisibleBitmap(int mFirstVisibleItem, int mVisibleItemCount) {

        for (int i = mFirstVisibleItem; i < mFirstVisibleItem + mVisibleItemCount; i++) {
            final String url = urls.get().get(i);
            Bitmap bitmap = get(url);
            ImageView mImageView;
            if (bitmap != null) {
                //缓存中存在该图片的话就设置给ImageView
                mImageView = (ImageView) mGridView.get().findViewWithTag(url);
                if (mImageView != null) {
                    mImageView.setImageBitmap(bitmap);
                }
            } else {
                //不存在的话就开启一个异步线程去下载
                ImageDownloadTask task = new ImageDownloadTask(this);
                mDownloadTaskList.add(task);
                task.execute(url);
            }
        }
    }

    /**
     * 取消所有的下载任务
     */
    public void cancelAllTask() {

        if (mDownloadTaskList != null) {
            for (int i = 0; i < mDownloadTaskList.size(); i++) {
                mDownloadTaskList.get(i).cancel(true);
            }
        }
    }


    /**
     * 从本地加载图片的异步task
     */
    static class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {

        private String url;
        private WeakReference<WineListAdapter> photoAdapter;

        public ImageDownloadTask(WineListAdapter photoAdapter) {
            this.photoAdapter = new WeakReference<WineListAdapter>(photoAdapter);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            //在后台开始下载图片
            url = params[0];
            Bitmap bitmap = photoAdapter.get().loadBitmap(url);
            if (bitmap != null) {
                //把下载好的图片放入LruCache中
                String key = url;
                photoAdapter.get().put(key, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //把下载好的图片显示出来
            ImageView mImageView = (ImageView) photoAdapter.get().mGridView.get().findViewWithTag(url);
            if (mImageView != null && bitmap != null) {
                mImageView.setImageBitmap(bitmap);
                photoAdapter.get().mDownloadTaskList.remove(this);//把下载好的任务移除
            }
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        //GridView停止滑动时,去加载可见项的图片
        if (scrollState == SCROLL_STATE_IDLE) {
            loadVisibleBitmap(mFirstVisibleIndex, mVisibleItemCount);
        } else {
            //GridView开始滑动时,取消所有加载任务
            cancelAllTask();
        }

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleIndex = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;

        // 第一次打开,加载可见项
        if (isFirstOpen && visibleItemCount > 0) {
            loadVisibleBitmap(mFirstVisibleIndex, mVisibleItemCount);
            isFirstOpen = false;
        }

    }

    private class ViewHolder {
        public ImageView im;
        public TextView tv;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值