Android高效加载大图、多图解决方案,有效避免程序OOM转载学习研究总结

本文总结了基于郭霖老师关于Android高效加载大图的文章,介绍了如何自定义压缩图片并优化内存使用,实现动态压缩网络获取的大图。文章详细阐述了关键的技术点,如解决InputStream流失效问题,通过改进Util类中的方法来成功加载不同比例压缩的图片。同时,展示了自定义Adapter类,实现了高效多图加载,包括内存缓存、异步任务管理和下载图片逻辑。

最近研究了郭霖老师的这篇Android高效加载大图的文章,从中学到了很多,也思考了很多。特写此文章将自己的所想结合前辈的文章一起来个总结;


郭老师一共用了两篇文章来介绍android高效加载大图,我在学习了两篇文章之后,将两篇文章结合在了一起,写了一个能自定义压缩图片的高效加载大图多图的Demo,

在参照前辈文章编写的时候,遇到一个关键的技术总结点:

  1. 在通过httpurlconnection从网络获取到输入流之后,一开始我是这样写的:
    public static Bitmap decodeSampledBitmapFromResource(InputStream is, int reqWidth, int reqHeight)
        {
            // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
            final BitmapFactory.Options options = new BitmapFactory.Options();
            //inJustDecodeBounds设置为true,将不返回实际的bitmap不给其分配内存空间而里面只包括一些解码边界信息即图片大小信息
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, options);
            // 调用上面定义的方法计算inSampleSize值
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            // 使用获取到的inSampleSize值再次解析图片
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeStream(is, null, options);
        }
    

        结果执行这个方法之后发现,无论怎么试,方法体返回的Bitmap对象都是null,最后通过debug模式,发现is流在第一次BitmapFactory.decodeStream(is, null, options)之后

就失效了,导致最后return时再调用解析时,已经不存在了。

        后来研究了BitmapFactory的方法,发现里面有一个decodeByteArray()方法,于是尝试了先将is流先转换成byte[]字节数组存放在方法内存中,这样就不会有失效的问题了,于是又了下面改进版的Util.java类:

public class Util
{
    /**
     * @param is
     * @param reqHeight
     * @param reqWidth
     * @return  
     * @throws IOException 
     * @Description:压缩图片
     */
    public static Bitmap getNewBitmap(InputStream is, int reqHeight, int reqWidth) throws IOException
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        byte[] bt = getBytes(is);
        BitmapFactory.decodeByteArray(bt, 0, bt.length, options);
        options.inSampleSize = getSampleSize(options, reqHeight, reqWidth);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeByteArray(bt, 0, bt.length, options);
    }

    /**
     * @param options
     * @param reqHeight
     * @param reqWidth
     * @return  
     * @Description:获取图片压缩的比率
     */
    private static int getSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth)
    {
        int height = options.outHeight;
        int width = options.outWidth;
        int sampleSize = 1;
        if(height > reqHeight || width > reqWidth)
        {
            int heightRatio = Math.round((float) height / (float) reqHeight);
            int widthRatio = Math.round((float) width / (float) reqWidth);
            sampleSize = heightRatio > widthRatio ? widthRatio : heightRatio;
        }
        Log.d("gu", sampleSize + "");
        return sampleSize;
    }

    /**
     * @param is
     * @return
     * @throws IOException  
     * @Description:将inputStream转换成byte[]
     */
    private static byte[] getBytes(InputStream is) throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len = -1;
        byte[] buffer = new byte[1024];
        while((len = is.read(buffer)) != -1)
        {
            baos.write(buffer, 0, len);
        }
        baos.close();
        return baos.toByteArray();
    }
}
        经过检验,果然解决了上述问题,并且成功的可以为每张从网络获取到的大图进行不同比列的动态压缩;

下面贴上我的Adapter:
public class MyAdapter extends ArrayAdapter
   
     implements OnScrollListener
{

    private GridView myGridView;

    private Set
    
      myAsyncTasks;

    private LruCache
     
       memoryCache;

    private int firstVisibleItem;

    private int visibleItemCount;

    // 记录是不是第一次进入应用,第一次不会激发onScrollStateChanged方法,所以要在onScroll方法中调用下载图片的方法,但是后面就不需要再onScroll中
    // 调用下载图片方法了
    private boolean isFirstEnter = true;

    public MyAdapter(Context context, int resource, String[] objects, GridView gridView)
    {
        super(context, resource, objects);
        myGridView = gridView;
        myAsyncTasks = new HashSet
      
       ();
        // 获取应用的最大运行内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        // 计算分配给LruCache的最大内存
        int cacheSize = maxMemory / 8;
        // 设置分配给LruCache的内存为应用运行最大内存的8分之一
        memoryCache = new LruCache
       
        (cacheSize)
        {
              @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                    return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                }

                @Override
                protected void entryRemoved(boolean evicted, String key,
                        Bitmap oldValue, Bitmap newValue) {
                    Log.v("tag", "hard cache is full , push to soft cache");
                   
                }
        };

        myGridView.setOnScrollListener(this);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        String url = getItem(position);
        // ViewHolder holder = null;
        View view = null;
        if(convertView == null)
        {
            // holder = new ViewHolder();
            view = LayoutInflater.from(getContext()).inflate(R.layout.item_layout, null);
            // convertView.setTag(holder);
        }
        else
        {
            // holder = (ViewHolder) convertView.getTag();
            view = convertView;
        }
        ImageView imageView = (ImageView) view.findViewById(R.id.img);
        // 为了防止异步任务导致图片加载之后出现顺序错乱的情况,为每一个imageView加入一个tag
        imageView.setTag(url);
        setImageView(url, imageView);
        return view;
    }

    /**
     * @param url
     * @param imageView  
     * @Description:设置图片,如果从缓存中读取到了图片就为它设置为读取到的网络图片,否则将其设置为默认图片
     */
    private void setImageView(String url, ImageView imageView)
    {
        Bitmap bitmap = getBitmapFromCache(url);
        if(bitmap != null)
        {
            imageView.setImageBitmap(bitmap);
        }
        else
        {
            imageView.setImageResource(R.drawable.ic_launcher);
        }
    }

    /**
     * @param key
     * @param bitmap  
     * @Description:如果缓存中还没有这个图片,就将这个图片加入到缓存当中
     */
    private void addBitmapToCache(String key, Bitmap bitmap)
    {
        if(getBitmapFromCache(key) == null)
        {
            memoryCache.put(key, bitmap);
        }
    }

    /**
     * @param key
     * @return  
     * @Description:根据url这个key从缓存中取出图片
     */
    private Bitmap getBitmapFromCache(String key)
    {
        return memoryCache.get(key);
    }

    /**
     *@Description: imageView的缓存类
     *@Author:Nate Robinson 
     *@Since:2015-2-11
     */
    // class ViewHolder
    // {
    // ImageView imageView;
    // }

    /**
     *@Description:  进行下载图片的一步任务类
     *@Author:Nate Robinson 
     *@Since:2015-2-11
     */
    class MyAsyncTask extends AsyncTask
        
          { private String imageUrl; @Override protected Bitmap doInBackground(String... params) { imageUrl = params[0]; Bitmap bitmap = downloadBitmap(imageUrl); if(bitmap != null) { addBitmapToCache(imageUrl, bitmap); } return bitmap; } @Override protected void onPreExecute() { Log.d("task", "start"); } @Override protected void onPostExecute(Bitmap result) { Log.d("task", "finish"); super.onPostExecute(result); ImageView imageView = (ImageView) myGridView.findViewWithTag(imageUrl); if(result != null && imageView != null) { imageView.setImageBitmap(result); } myAsyncTasks.remove(this); } /** * @return * @Description:下载任务 */ private Bitmap downloadBitmap(String url) { Bitmap bitmap = null; HttpURLConnection httpURLConnection = null; try { URL imageUrl = new URL(url); httpURLConnection = (HttpURLConnection) imageUrl.openConnection(); httpURLConnection.setConnectTimeout(5 * 1000); httpURLConnection.setReadTimeout(10 * 1000); bitmap = Util.getNewBitmap(httpURLConnection.getInputStream(), 90, 90); } catch(Exception e) { e.printStackTrace(); } finally { // 关闭连接 httpURLConnection.disconnect(); } return bitmap; } } /** * @Description:结束所有在进行中异步任务 */ public void cancelAllTask() { if(myAsyncTasks != null) { for(MyAsyncTask task : myAsyncTasks) { task.cancel(false); } } } private void loadBitmaps(int firstVisibleItem, int visibleItemCount) { try { for(int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) { String url = com.gu.demo.Images.imageUrls[i]; Bitmap bitmap = getBitmapFromCache(url); if(bitmap == null) { MyAsyncTask task = new MyAsyncTask(); task.execute(url); myAsyncTasks.add(task); } else { // 通过之前的tag从缓存中再次取出ImageView对象 ImageView imageView = (ImageView) myGridView.findViewWithTag(url); if(bitmap != null && imageView != null) { imageView.setImageBitmap(bitmap); } } } } catch(Exception e) { e.printStackTrace(); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 仅当gridVIew处于静止的时候才去下载图片 if(scrollState == SCROLL_STATE_IDLE) { loadBitmaps(firstVisibleItem, visibleItemCount); } else { // 取消所有的下载任务 cancelAllTask(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; this.visibleItemCount = visibleItemCount; if(isFirstEnter && visibleItemCount > 0) { loadBitmaps(firstVisibleItem, visibleItemCount); isFirstEnter = false; } } } 
        
       
      
     
    
   
       其余代码都是参照前辈的。
       写这篇文章的目的,一是给自己一个总结,二是将前辈两篇文章的核心思想结合在一个Demo中,这样更能形象直接的理解学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值