• Volley该框架使用了大量的请求图片


    尊重原创 http://write.blog.csdn.net/postedit/26142025

    代码下载:http://download.csdn.net/detail/yuanzeyao2008/7363999


    假设你希望了解Volley框架的原理,欢迎阅读

    volley源代码框架浅析(一)

    volley源代码框架浅析(二)

    volley源代码框架浅析(三)


    前面已经说过,Volley框架能够用来请求String,XML,Json,Image以及一些自己定义的类型,这篇文章主要解说使用Volley请求大量图片。并使用GridView展示出来。这个功能在非常多应用中都会用到,如PPS等类型的播放器,淘宝等等。

    像这类应用无非就是要解决一下问题:

    1、避免OOM,在使用GridView等控件显示大量图片时,假设对缓存处理不当,是很easy出现OOM的。
    2、GridView错位显示,比方GridView中的某个ImageView等待某一个图片(没有返回)。然而你此时又滑动了GridView,那么此时的GridView中的ImageView须要的是另外一张图片,假设你之前的图片已经返回回来是不能显示出来的。
    把这两个问题攻克了,此类问题也就差点儿相同了,剩下的就是效率问题了,这点Volley已经处理好了,我们须要处理的就是以上两点


    1、解决OOM
    我就顺便讲解说决OOM的经常使用策略吧
    OOM出现的情况可能是显示的图片非常大可是数量不是非常多。也有可能是显示的图片非常多可是图片不是非常大,我们先看第一种情况
    http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
    我们直接看官网怎么处理这个问题。我翻译了一下


    对于某些图片。分辨率往往比我们须要的高,比方我们使用Galley控件展示我们使用手机拍摄的照片时,这些照片的分辨率都比我们的屏幕分辨率高,所以在显示的时候我们应该减少这些图片的分辨率,由于此时高分辨率的图片在视觉上不会给我们带来不论什么区别。反而会占用我们移动设备的珍贵内存。以下我们就学习怎样将高分辨率的图片减少它的分辨率

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
    int imageHeight = options.outHeight;
    int imageWidth = options.outWidth;
    String imageType = options.outMimeType;

    我们在使用BitmapFactory读取图片数据的时候,假设我们把options.inJustDecodeBounds设置为true,那么BitmapFactory仅仅会读取图片的高度和宽度等信息。不会将图片数据读入内存

    options另一个属性options.inSampleSize,BitmapFactory在解析图片数据的时候,会将图片的宽度和高度变为原来的1/inSampleSize,这样图片大大小就会变为1/(inSampleSize*inSampleSize),比方inSampleSize=2 那么图片大小会变为原来的1/4
    那么inSampleSize这个值怎么获取呢。通过下面算法就可以

    /**
    	optiosns 就是上面我得到的options
    	reqWidth 就是我们须要图片的宽度
    	reqHeight 就是我们须要图片的高度
    */
    public static int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        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;
    
            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
    
        return inSampleSize;
    }

    解析图片至须要的宽度和高度

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
            int reqWidth, int reqHeight) {
    
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
    	//首先将这个值设置为true, 所以仅仅会获取高度和宽度
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
    
        // 依据须要的宽度和高度  图片的实际高度和宽度  计算缩小比例
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
        // 然后将该值设置为false,真正解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    通过以上的步骤遍能够将图片缩小至某一大小,从而避免OOM


    再来讲讲另外一种OOM,这样的OOM是由于有太多的图片,所以解决方式就是使用内存缓存和磁盘缓存
    在Andorid中有两个类
    LruCache和DiskLruCache这个两个类分别相应内存缓存和磁盘缓存
    详细使用见
    http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
    到此为止OOM的问题就攻克了。以下看看错位问题
    方案一:
    对于每个请求的URL就设置到ImageView的tag中如imageView.setTag(url).当某一个请求成功返回一张图片时,推断ImageView的tag是否是当前请求的url
    假设是的。那么就显示出来。并增加缓存。假设不是的,那么只增加缓存
    方案二:
    一旦GridView滑动。那么取消全部的请求,当GridView停止时。里面開始请求能够看见的ImageView须要的图片,今天的样例我就是使用另外一种方案,须要设置OnScrollListener

    好了 我们如今開始写代码吧

    先总结一下Volley框架的使用步骤吧
    (1) 初始化RequestQueue 
    (2) 创建自己的Request
    (3) 将Request增加到RequestQueue中。
    可是对于图片的请求步骤略微有点不同,图片的请求涉及到ImageLoader实现的,事实上ImageLoader也是通过以上三步实现图片请求的
    以下我就在Appliaction中初始化Volley使用的环境吧


    public class VolleyApplication extends Application 
    {
      private static final String TAG = "VolleyApplication";
      //请求队列
      private RequestQueue mRequestQueue;
      private static  VolleyApplication instance;
      //用于图片请求
      private ImageLoader mImageLoader;
      //使用单例模式
      public static VolleyApplication getInstance()
      {
        return instance;
      }
      
      public RequestQueue getRequestQueue()
      {
        return mRequestQueue;
      }
      
      public ImageLoader getImageLoader()
      {
        return mImageLoader;
      }
      @Override
      public void onCreate() 
      {
        super.onCreate();
    	//初始化内存缓存文件夹
        File cacheDir = new File(this.getCacheDir(), "volley");
    	/**
    	初始化RequestQueue,事实上这里你能够使用Volley.newRequestQueue来创建一个RequestQueue,直接使用构造函数能够定制我们须要的RequestQueue,比方线程池的大小等等
    	*/
        mRequestQueue=new RequestQueue(new DiskBasedCache(cacheDir), new BasicNetwork(new HurlStack()), 3);
        
        instance=this;
        
    	//初始化图片内存缓存
        MemoryCache mCache=new MemoryCache();
    	//初始化ImageLoader
        mImageLoader =new ImageLoader(mRequestQueue,mCache);
    	//假设调用Volley.newRequestQueue,那么以下这句能够不用调用
        mRequestQueue.start();
      }
    }

    使用LrcCache实现的一个图片缓存,这个基本上能够通用

    public class MemoryCache implements ImageCache 
    {
      private static final String TAG = "MemoryCache";
      private LruCache<String, Bitmap> mCache;
      
      public MemoryCache()
      {
    	//这个取单个应用最大使用内存的1/8
        int maxSize=(int)Runtime.getRuntime().maxMemory()/8;
        mCache=new LruCache<String, Bitmap>(maxSize){
          @Override
          protected int sizeOf(String key, Bitmap value) {
    	  //这种方法一定要重写,不然缓存没有效果
            return value.getHeight()*value.getRowBytes();
          }
        };
      }
    
      @Override
      public Bitmap getBitmap(String key) {
        return mCache.get(key);
      }
    
      @Override
      public void putBitmap(String key, Bitmap value) {
        mCache.put(key, value);
      }
    }

    那么開始写代码吧

    (1) 首先给出Activity的布局吧

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
         <GridView   
            android:id="@+id/grid_image"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:columnWidth="128dip"  
            android:stretchMode="columnWidth"  
            android:numColumns="2"  
            android:verticalSpacing="1dip"  
            android:horizontalSpacing="1dip"
            android:gravity="center"  
            ></GridView> 
    </LinearLayout>

    (2) 然后每一个Item的布局

    <?

    xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView android:id="@+id/photo" android:layout_width="128dip" android:layout_height="128dip" android:src="@drawable/empty_photo" android:layout_centerInParent="true" /> </RelativeLayout>


    (3) Activity代码

    public class ImageActivity extends Activity 
    {
      private static final String TAG = "ImageActivity";
      private GridView mGridView;
      private ImageAdapter adapter;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.imagelayout);
        initViews();
      }
      
      private void initViews()
      {
        Log.i(TAG,"initViews");
        mGridView=(GridView)this.findViewById(R.id.grid_image);
        adapter=new ImageAdapter(this,mGridView);
        mGridView.setAdapter(adapter);
        
      }
    }

    (4) 最重要部分代码

    public class ImageAdapter extends BaseAdapter implements OnScrollListener
    {
      private static final String TAG = "ImageAdapter";
      private Context context;
      private String[] items=Images.imageThumbUrls;
      private GridView mGridView;
      
      /**
       * 标识是否是第一次运行,假设是第一次运行 onScrollStateChanged是不调用的
       */
      private boolean isFirstEnter;
      /**
       * 第一个能够看见的item
       */
      private int firstSeeItem;
      
      /**
       * 记录上一次能够看见的第一个,由于假设已经到顶部。向下滑动GridView也会运行onScrollStateChanged 所以第一个能够见的没有变化,那么就不运行
       */
      private int orifirstItem;
      /**
       * 能够看见item的总数
       */
      private int totalSeeItem;
      
      public ImageAdapter(Context context,GridView mGridView)
      {
        this.context=context;
        this.mGridView=mGridView;
        //注冊这个是为了在滑动的时候停止下载图片,不然非常卡
        mGridView.setOnScrollListener(this);
        isFirstEnter=true;
      }
    
      @Override
      public int getCount() {
        return items.length;
      }
    
      @Override
      public Object getItem(int position) {
        return items[position];
      }
    
      @Override
      public long getItemId(int position) {
        return position;
      }
    
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        Log.v(TAG, "imagedown--->getView");
        ImageView imgView=null;
        if(convertView==null)
        {
          convertView=LayoutInflater.from(context).inflate(R.layout.imageitems, null);
        }
        imgView=(ImageView)convertView.findViewById(R.id.photo);
        imgView.setImageResource(R.drawable.empty_photo);
        //通过GridView的findViewByTag方法找到该View,防止错位发生
        imgView.setTag(items[position]);
        
        return convertView;
      }
    
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 
      {
        Log.v(TAG, "imagedown--->onScroll");
        firstSeeItem=firstVisibleItem;
        totalSeeItem=visibleItemCount;
        
        if(isFirstEnter && visibleItemCount>0)
        {
          orifirstItem=firstVisibleItem;
          startLoadImages(firstSeeItem,totalSeeItem);
          isFirstEnter=false;
        }
      }
    
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) 
      {
        Log.v(TAG, "imagedown--->onScrollStateChanged");
        if(orifirstItem!=firstSeeItem)
        {
          if(scrollState==SCROLL_STATE_IDLE)
          {
            startLoadImages(firstSeeItem,totalSeeItem);
            orifirstItem=firstSeeItem;
          }else
          {
            ImageUtils.cancelAllImageRequests();
          }
        }
       
        
      }
      /**
      開始下载图片
      first就是第一个能够看见的position
      total就是能够看见个Item个数
      */
      private void startLoadImages(int first,int total)
      {
        Log.v(TAG, "imagedown--->startLoadImages,first-->"+first+",total-->"+total);
        for(int i=first;i<first+total;i++)
        {
          ImageUtils.loadImage(items[i], mGridView);
        }
      }
    }

    (5) 请求图片工具类

    public class ImageUtils
    {
      private static final String TAG = "ImageUtils";
      
      public static void loadImage(final String url,final GridView mGridView)
      {
          
          ImageLoader imageLoader=VolleyApplication.getInstance().getImageLoader();
          ImageListener listener=new ImageListener() {
            ImageView tmpImg=(ImageView)mGridView.findViewWithTag(url);
            @Override
            public void onErrorResponse(VolleyError arg0) {
              //假设出错。则说明都不显示(简单处理),最好准备一张出错图片
              tmpImg.setImageBitmap(null);
            }
            
            @Override
            public void onResponse(ImageContainer container, boolean arg1) {
              
              if(container!=null)
              {
                 tmpImg=(ImageView)mGridView.findViewWithTag(url);
                if(tmpImg!=null)
                {
                  if(container.getBitmap()==null)
                  {
                    tmpImg.setImageResource(R.drawable.empty_photo);
                  }else
                  {
                    tmpImg.setImageBitmap(container.getBitmap());
                  }
                }
              }
            }
          };
          ImageContainer newContainer=imageLoader.get(url, listener,128,128);
      }
      
      /**
       *  取消图片请求
       */
      public static void cancelAllImageRequests() {
        ImageLoader imageLoader = VolleyApplication.getInstance().getImageLoader();
        RequestQueue requestQueue =   VolleyApplication.getInstance().getRequestQueue();
        if(imageLoader != null && requestQueue!=null){
          int num = requestQueue.getSequenceNumber();
    	  //这种方法是我自己写的,Volley里面是没有的。所以仅仅能使用我给的Volley.jar才有这个函数
          imageLoader.drain(num);
        }
      }
    
    }

    好了 今天就写到这里吧,有什么问题欢迎留言讨论。代码在前面已经提供下载了。。


    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    NYOJ-301递推求值
    二叉树
    从c到c++
    NYOJ-93汉诺塔(三)
    历届试题 最大子阵
    最大子序列和最大子矩阵
    【贪心】电视节目安排
    【贪心】线段覆盖
    【贪心】取数游戏
    【贪心】智力大冲浪
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4632646.html
Copyright © 2020-2023  润新知