Android网络图片加载内存溢出(OOM)解决方案

1 篇文章 0 订阅
1 篇文章 0 订阅

最近在做的项目在加载大量网络图片时遇到了OOM,在网上找了一些资料和请教公司高级工程师,对代码进行了优化。将大量图片直接加载到内存中,是造成OOM的主要原因。
解决方法:
添加本地缓存,不直接从网络加载图片到内存。将图片缓存到本地,每次都从本地获取图片,如果本地没有,再从网络获取。

本地缓存

开启线程,将网络图片下载到本地SD卡。

/**
*下载文件到SD卡指定路径
*/
private static void downLoadImage2SDCard(final String urlString, final String filepath) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                URL url = new URL(urlString);
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5000);
                con.setRequestMethod("POST");
                int code = con.getResponseCode();
                // 请求成功
                if (code != 200) {
                    Log.e(TAG, "Connection fail : " + code);
                } else {
                    InputStream is = con.getInputStream();
                    FileOutputStream fos = new FileOutputStream(filepath);
                    byte[] b = new byte[1024];
                    while (is.read(b) != -1) {
                        fos.write(b);
                    }
                    is.close();
                    fos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

自定义Adapter

convertView + ViewHolder实现ListView的性能优化
1. 复用convertView,减少对象的创建。
2. 使用ViewHolder类,避免反复使用findViewById的耗时操作。

public View getView(int position, View convertView, ViewGroup parent) {
    RankListItem listItem = null;
    if (convertView == null) {
        listItem = new RankListItem();
        LayoutInflater layoutInflater = LayoutInflater.from(ctxt);
        convertView = layoutInflater.inflate(R.layout.list_item_layout_moments_rank, null);
        listItem.avatar = (ImageView) convertView.findViewById(R.id.rank_item_im_avatar);
        listItem.username = (TextView) convertView.findViewById(R.id.rank_item_tv_username);
        convertView.setTag(listItem);
    } else {
        listItem = (RankListItem) convertView.getTag();
    }
    // 绑定数据
    String imageName = url.substring(url.lastIndexOf("/"));
    String filePath = FileUtils.getImgFolderPath() + imageName;
    File imageFile = new File(filePath);
    if (!imageFile.exists()) {
        String imageUrl = BaseUrls.ROOT_URL + url;
        downLoadImage2SDCard(imageUrl, imageFile);
    } else {
        Log.i(TAG, "filePath = " + filePath);
        Bitmap bitmap = FileUtils.getLocalImage(filePath);
        if (bitmap == null) {
            listItem.avatar.setImageResource(R.drawable.my_icon_user);
        } else {
            listItem.avatar.setImageBitmap(bitmap);
        }
    }
    listItem.username.setText(jsonObject.getString("username"));
    return convertView;
}

public final class RankListItem {
        public ImageView avatar;
        public TextView username;
    }

图片压缩

压缩图片到指定大小

/**
 * 获取指定大小的本地图片
 */
public static Bitmap getLocalImage(String filePath) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);
    int height = options.outHeight;
    int width = options.outWidth;
    int inSampleSize = 1;
    int reqHeight = 600;
    int reqWidth = 600;
    if (height > reqHeight || width > reqWidth) {
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    options.inSampleSize = inSampleSize;
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(filePath, options);
}

运行效果:
运行效果

这是本人解决OOM的几个策略,在此记录学习。

性能优化:
使用线程池管理线程,防止无限制的创建线程。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

private void getLocalImage(final String filePath, final ImageView imgView) {
    fixedThreadPool.execute(new Runnable() {
        @Override
        public void run() {
            final Bitmap bitmap = FileUtils.getLocalImage(filePath);
            if (bitmap != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        imgView.setImageBitmap(bitmap);
                    }
                });
            }
        }
    });
}

private void downLoadImage2SDCard(final String iamgeUrl) {
        fixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                InputStream is = HttpClientHelp.getFileFromServerByHttpClient(iamgeUrl);
                String filepath = FileUtils.getImgFolderPath() + iamgeUrl.substring(iamgeUrl.lastIndexOf("/"));
                if (is != null) {
                    try {
                        FileOutputStream fos = new FileOutputStream(filepath);
                        byte[] b = new byte[1024];
                        while (is.read(b) != -1) {
                            fos.write(b);
                        }
                        is.close();
                        fos.close();
                        mHandler.obtainMessage(MSG_DOWNLOAD_IMAGE);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

newFixedThreadPool(5);
创建一个定长为5的线程池,可控制线程最大并发数为5,超出的线程会在队列中等待。
对线程进行管理,重用存在的线程,减少对象创建、销毁,效控制最大并发线程数,提高系统资源的使用率。

最后,封装成一个dispaly方法,参数为网络图片路径url和要设置的ImageView

private void displayImage(final String iamgeUrl, final ImageView imageView) {
    fixedThreadPool.execute(new Runnable() {
        @Override
        public void run() {
            String imageName = iamgeUrl.substring(iamgeUrl.lastIndexOf("/"));
            String filepath = FileUtils.getImgFolderPath() + imageName;
            File imageFile = new File(filepath);
            if (!imageFile.exists()) {
                // 获取网络图片输入流
                InputStream is = HttpClientHelp.getFileFromServerByHttpClient(iamgeUrl);
                if (is != null) {
                    try {
                        FileOutputStream fos = new FileOutputStream(filepath);
                        byte[] b = new byte[1024];
                        while (is.read(b) != -1) {
                            fos.write(b);
                        }
                        is.close();
                        fos.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            // 获取本地图片的Bitmap
            final Bitmap bitmap = FileUtils.getLocalImage(filepath);
            if (bitmap != null) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmap);
                    }
                });
            }
        }
    });
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值