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);
    }
  }

}

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

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

时间: 2024-08-28 06:39:31

Volley该框架使用了大量的请求图片的相关文章

Android 学习笔记之Volley开源框架解析(一)

PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍...   1.Http请求...   这里只是简单的说一下Http请求的过程...非常的简单...首先是发送Request..然后服务器在获取到Request请求后会对其进行相应的处理,然后以Response的形式进行返回,然后分配Response,即谁发送的请求,那么响应就分配给谁... 2.Volley简单介绍...   这里只是先简单的说一下Volley,Volley框架

Volley网络框架完全解析(缓存篇)

在上一篇中讲完了Volley框架怎么使用,那么这篇就来讲讲Volley框架的缓存机制 我们看Volley内部源码发现: Volley框架内部自己处理了DiskBasedCache硬盘缓存,但是没有处理LruCache内存缓存,因为一般在处理图片的问题上才更多的用到LruCache缓存,但是它提供了一个ImageCache接口供我们自己实现,该接口默认需要实现两个方法:getBitmap(String key)和putBitmap(String key, Bitmap bitmap),由此可见,这

Android开发之Volley网络通信框架

今天用了一下Volley网络通信框架,感觉挺好用的,写个博客记录一下使用方法,方便以后VC. Volley(Google提供的网络通信库,能使网络通信更快,更简单,更健壮.) 功能模块: 1. JSON,图像等的异步下载 --------------------------------------------------------------------------------------- 2. 网络请求的排序(scheduling) ----------------------------

【Spring学习笔记-MVC-5】利用spring MVC框架,实现ajax异步请求以及json数据的返回

作者:ssslinppp      时间:2015年5月26日 15:32:51 1. 摘要 本文讲解如何利用spring MVC框架,实现ajax异步请求以及json数据的返回. Spring MVC与json参考文章:[spring学习笔记-mvc-3]返回json数据-方式1  和 [spring学习笔记-mvc-4]返回json数据-方式2. 使用到的技术主要如下: Ajax:使用JQuery 提供的ajax:==>需要引入jquery.min.js文件: Spring MVC: Jso

scrapy框架的日志等级和请求传参, 优化效率

目录 scrapy框架的日志等级和请求传参, 优化效率 Scrapy的日志等级 请求传参 如何提高scripy的爬取效率 scrapy框架的日志等级和请求传参, 优化效率 Scrapy的日志等级 在使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息. 日志信息的种类: ERROR : 一般错误 WARNING : 警告 INFO : 一般的信息 DEBUG : 调试信息 设置日志信息指定输出: 在settings.py配置文件中,加

安卓网络请求图片到图片的三级缓存技术(内存缓存,本地缓存,网络缓存)

安卓网络请求图片,对于我们来说并不陌生,因为每个应用都有可能会用到这一技术.通常情况下,我们第一次都是从网络上请求图片资源,然后将 图片资源保存到内存和本地,下一次动态显示图片的时候就不需要再从网络上请求图片资源了,直接从本地或者内存中获取就可以了.这就涉及到图片 的三级缓存技术,分别是内存缓存,本地缓存,网络缓存. 缓存的流程图: 首先我们定义一个类叫ClassLoader: package com.jsako.showprodinfodemo; import java.io.FileOutp

网络请求图片

效果图: 代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(50, 100, 200, 200)]; imageView.backgroundColor=[UIColor r

微信机器人开发SDK使用教程--请求图片或视频消息的详细内容

微信机器人开发SDK使用教程--请求图片或视频消息的详细内容 case "RequestTalkDetailTask": {// 请求图片或视频消息的详细内容 log.debug("websocket:msgtype=RequestTalkDetailTask....."); requestTalkDetailTaskWebsocketHandler.handleMsg(ctx, vo,contentJsonStr); break; } package com.ju

Volley网络请求框架简析——Android网络请求框架(三)

题记-- 人来到这个世界上,只有两件事情,生与死, 一件事完了,另一件事还急什么? 有缘而来,无缘而去, 识自本心,见自本性 不起妄缘,无心无为 自由自在,动静自如 冷暖自知,则是修行 1.初始化一个消息请求队列以及网络请求工具类对象 /** * Created by androidlongs on 16/7/1. * 网络请求访问框架 */ public class VollyRequestUtils { /** * Volley框架使用工具类对象 */ private static Voll