Android网络框架Volley

来源:互联网 发布:tgp游戏优化 编辑:程序博客网 时间:2024/05/06 07:42
volley

目录(?)[+]

Volley是Google I/O 2013推出的网络通信库,在volley推出之前我们一般会选择比较成熟的第三方网络通信库,如:
  • android-async-http

  • retrofit

  • okhttp

他们各有优劣,之前个人则比较喜欢用android-async-http, 如今Google推出了官方的针对Android平台上的网络通信库,能使网络通信更快,更简单,更健壮,Volley在提供了高性能网络通讯功能的同时,对网络图片加载也提供了良好的支持,完全可以满足简单REST客户端的需求, 我们没有理由不跟上时代的潮流

使用Volley

下载Volley源码并build jar包。

$ git clone https://android.googlesource.com/platform/frameworks/volley$ cd volley$ android update project -p$ ant jar

然后把生成的jar包引用到我们的项目中,extras目录下则包含了目前最新的volley源码。

说明

此Demo主要介绍了日常网络开发常用的基本功能,但volley的扩展性很强,可以根据需要定制你自己的网络请求。

volley视频地址: http://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded


以上是在Google IO的演讲上ppt的配图,从上面这张图我们可以看出,volley适合快速,简单的请求(Json对象,图片加载)。

volley的特性:

  • JSON,图像等的异步下载;
  • 网络请求的排序(scheduling)
  • 网络请求的优先级处理
  • 缓存
  • 多级别取消请求
  • 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)

接下来,我们来学习简单的使用下volley给我提供的API吧。

1.首先拿到一个请求队列(RequestQueue只需要一个实例即可,不像AsyncTask每次使用都要new一个)

[java] view plaincopy
  1. // 初始化RequestQueue一个activity只需要一个  
  2.     private void initRequestQueue() {  
  3.         mQueue = Volley.newRequestQueue(getApplicationContext());  
  4.     }  

2.实现volley的异步请求类(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest

由于用法都相差不大,我就不一一举例了,举几个常用有代表性的例子:

以下代码是StringRequest的get请求:

[java] view plaincopy
  1. // get请求  
[java] view plaincopy
  1. private void loadGetStr(String url) {  
  2.   
  3.     StringRequest srReq = new StringRequest(Request.Method.GET, url,  
  4.             new StrListener(), new StrErrListener()) {  
  5.   
  6.         protected final String TYPE_UTF8_CHARSET = "charset=UTF-8";  
  7.   
  8.         // 重写parseNetworkResponse方法改变返回头参数解决乱码问题  
  9.         // 主要是看服务器编码,如果服务器编码不是UTF-8的话那么就需要自己转换,反之则不需要  
  10.         @Override  
  11.         protected Response<String> parseNetworkResponse(  
  12.                 NetworkResponse response) {  
  13.             try {  
  14.                 String type = response.headers.get(HTTP.CONTENT_TYPE);  
  15.                 if (type == null) {  
  16.                     type = TYPE_UTF8_CHARSET;  
  17.                     response.headers.put(HTTP.CONTENT_TYPE, type);  
  18.                 } else if (!type.contains("UTF-8")) {  
  19.                     type += ";" + TYPE_UTF8_CHARSET;  
  20.                     response.headers.put(HTTP.CONTENT_TYPE, type);  
  21.                 }  
  22.             } catch (Exception e) {  
  23.             }  
  24.             return super.parseNetworkResponse(response);  
  25.         }  
  26.     };  
  27.     srReq.setShouldCache(true); // 控制是否缓存  
  28.     startVolley(srReq);  
  29. }  

以下代码是JsonObjectRequest的post请求:
[java] view plaincopy
  1. // post请求  
  2. private void loadPostJson(String url) {  
  3.     // 第二个参数说明:  
  4.     // Constructor which defaults to GET if jsonRequest is null, POST  
  5.     // otherwise.  
  6.     // 默认情况下设成null为get方法,否则为post方法。  
  7.     JsonObjectRequest srReq = new JsonObjectRequest(url, null,  
  8.             new JsonListener(), new StrErrListener()) {  
  9.   
  10.         @Override  
  11.         protected Map<String, String> getParams() throws AuthFailureError {  
  12.             Map<String, String> map = new HashMap<String, String>();  
  13.             map.put("w""2459115");  
  14.             map.put("u""f");  
  15.             return map;  
  16.         }  
  17.     };  
  18.     srReq.setShouldCache(false); // 控制是否缓存  
  19.     startVolley(srReq);  
  20. }  

大家注意看的话,无论是JsonObjectReques的postt还是StringRequest的get都需要传入两个监听函数一个是成功一个是失败,成功监听他们会返回相应类型的数据:

[java] view plaincopy
  1. // Str请求成功回调  
  2. private class StrListener implements Listener<String> {  
  3.   
  4.     @Override  
  5.     public void onResponse(String arg0) {  
  6.         Log.e(Tag, arg0);  
  7.   
  8.     }  
  9.   
  10. }  
  11.   
  12. // Gson请求成功回调  
  13. private class GsonListener implements Listener<ErrorRsp> {  
  14.   
  15.     @Override  
  16.     public void onResponse(ErrorRsp arg0) {  
  17.         Toast.makeText(mContext, arg0.toString(), Toast.LENGTH_LONG).show();  
  18.     }  
  19.   
  20. }  
  21. // 共用失败回调  
  22. private class StrErrListener implements ErrorListener {  
  23.   
  24.     @Override  
  25.     public void onErrorResponse(VolleyError arg0) {  
  26.         Toast.makeText(mContext,  
  27.                 VolleyErrorHelper.getMessage(arg0, mContext),  
  28.                 Toast.LENGTH_LONG).show();  
  29.     }  
  30.   
  31. }  
接下来是ImageRequest
[java] view plaincopy
  1. /** 
  2.  * 第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩, 
  3.  * 指定成0的话就表示不管图片有多大,都不会进行压缩。 
  4.  *  
  5.  * @param url 
  6.  *            图片地址 
  7.  * @param listener 
  8.  * @param maxWidth 
  9.  *            指定允许图片最大的宽度 
  10.  * @param maxHeight 
  11.  *            指定允许图片最大的高度 
  12.  * @param decodeConfig 
  13.  *            指定图片的颜色属性,Bitmap.Config下的几个常量. 
  14.  * @param errorListener 
  15.  */  
  16. private void getImageRequest(final ImageView iv, String url) {  
  17.     ImageRequest imReq = new ImageRequest(url, new Listener<Bitmap>() {  
  18.   
  19.         @Override  
  20.         public void onResponse(Bitmap arg0) {  
  21.             iv.setImageBitmap(arg0);  
  22.         }  
  23.     }, 6060, Bitmap.Config.ARGB_8888, new StrErrListener());  
  24.     startVolley(imReq);  
  25. }  
看到现在大家肯定会疑惑写了这么多不同类型的Request到底如何运行?接下请看:

[java] view plaincopy
  1. // 添加及开始请求  
  2. private void startVolley(Request req) {  
  3.   
  4.     // 设置超时时间  
  5.     // req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));  
  6.     // 将请求加入队列  
  7.     mQueue.add(req);  
  8.     // 开始发起请求  
  9.     mQueue.start();  
  10. }  

volley不仅提供了这些请求的方式,还提供了加载图片的一些方法和控件:

比如我们一个列表需要加载很多图片我们可以使用volley给我们提供的ImageLoader( ImageLoader比ImageRequest更加高效,因为它不仅对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。

[java] view plaincopy
  1. public class ImageAdapter extends ArrayAdapter<String> {  
  2.       
  3.     private RequestQueue mQueue;  
  4.     private ImageLoader mImageLoader;  
  5.   
  6.     public ImageAdapter(Context context, List<String> objects) {  
  7.         super(context, 0, objects);  
  8.         mQueue = Volley.newRequestQueue(getContext());  
  9.         mImageLoader = new ImageLoader(mQueue, new BitmapCache());  
  10.     }  
  11.       
  12.     @Override  
  13.     public View getView(int position, View convertView, ViewGroup parent) {  
  14.         String url = getItem(position);  
  15.         ImageView imageView;  
  16.         if (convertView == null) {  
  17.             imageView = new ImageView(getContext());  
  18.         } else {  
  19.             imageView = (ImageView) convertView;  
  20.         }  
  21.         // getImageListener(imageView控件对象,默认图片地址,失败图片地址);  
  22.         ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete);  
  23.         // get(图片地址,listener,宽,高);自动帮你处理图片的宽高再也不怕大图片的oom了  
  24.         mImageLoader.get(url, listener,100,200);  
  25.         return imageView;  
  26.     }  
  27.   
  28. }  
当然还需要重写ImageCache这个类 //使用LruCache再也不用怕加载多张图片oom了

[java] view plaincopy
  1. public class <span style="font-family: Arial;">BitmapCache</span><span style="font-family: Arial;"extends LruCache<String, Bitmap> implements ImageCache {</span>  
  2.     // LruCache 原理:Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 当cache已满的时候加入新的item时,在队列尾部的item会被回收。  
  3.     // 解释:当超出指定内存值则移除最近最少用的图片内存  
  4.     public static int getDefaultLruCacheSize() {  
  5.         // 拿到最大内存  
  6.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  7.         // 拿到内存的八分之一来做图片内存缓存  
  8.         final int cacheSize = maxMemory / 8;  
  9.   
  10.         return cacheSize;  
  11.     }  
  12.   
  13.     public BitmapLruCache() {  
  14.         this(getDefaultLruCacheSize());  
  15.     }  
  16.   
  17.     public BitmapLruCache(int sizeInKiloBytes) {  
  18.         super(sizeInKiloBytes);  
  19.     }  
  20.   
  21.     @Override  
  22.     protected int sizeOf(String key, Bitmap value) {  
  23.         return value.getRowBytes() * value.getHeight() / 1024;  
  24.     }  
  25.   
  26.     @Override  
  27.     public Bitmap getBitmap(String url) {  
  28.         return get(url);  
  29.     }  
  30.   
  31.     @Override  
  32.     public void putBitmap(String url, Bitmap bitmap) {  
  33.         put(url, bitmap);  
  34.     }  
  35. }  

Volley还提供的加载图片的控件com.android.volley.NetworkImageView。(这个控件在被从父控件detach的时候,会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题,而且NetworkImageView还会根据你对图片设置的width和heigh自动压缩该图片不会产生多的内存,还有NetworkImageView在列表中使用不会图片错误

[html] view plaincopy
  1. <com.android.volley.toolbox.NetworkImageView  
  2.     android:id="@+id/network_image_view"  
  3.     android:layout_width="100dp"  
  4.     android:layout_height="100dp" />  
使用方法:

[java] view plaincopy
  1. private void networkImageViewUse(NetworkImageView iv, String url) {  
  2.         ImageLoader imLoader = new ImageLoader(mQueue, new BitmapLruCache());  
  3.         iv.setDefaultImageResId(R.drawable.ic_launcher);  
  4.         iv.setErrorImageResId(R.drawable.ic_launcher);  
  5.         iv.setImageUrl(url, imLoader);  
  6.     }  

我们说了这么多都是请求,那么如何取消请求呢?

1.activity自动销毁时它会自定取消所有请求。

2.给请求设置标签:

[java] view plaincopy
  1. request.setTag("My Tag");    

取消所有指定标记的请求:

[java] view plaincopy
  1. request.cancelAll("My Tag");    
Volley的架构设计:



其中蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。


接下来我们要看看如何把volley使用到实战项目里面,我们先考虑下一些问题:

从上一篇来看 mQueue 只需要一个对象即可,new RequestQueue对象对资源一种浪费,我们应该在application,以及可以把取消请求的方法也在application进行统一管理,看以下代码:

[java] view plaincopy
  1. package com.chronocloud.lib.base;  
  2.   
  3. import android.app.Application;  
  4. import android.text.TextUtils;  
  5.   
  6. import com.android.volley.Request;  
  7. import com.android.volley.RequestQueue;  
  8. import com.android.volley.VolleyLog;  
  9. import com.android.volley.toolbox.Volley;  
  10.   
  11. public class ApplicationController extends Application {  
  12.   
  13.     /** 
  14.      * Log or request TAG 
  15.      */  
  16.     public static final String TAG = "VolleyPatterns";  
  17.   
  18.     /** 
  19.      * Global request queue for Volley 
  20.      */  
  21.     private RequestQueue mRequestQueue;  
  22.   
  23.     /** 
  24.      * A singleton instance of the application class for easy access in other 
  25.      * places 
  26.      */  
  27.     private static ApplicationController sInstance;  
  28.   
  29.     @Override  
  30.     public void onCreate() {  
  31.         super.onCreate();  
  32.   
  33.         // initialize the singleton  
  34.         sInstance = this;  
  35.     }  
  36.   
  37.     /** 
  38.      * @return ApplicationController singleton instance 
  39.      */  
  40.     public static synchronized ApplicationController getInstance() {  
  41.         return sInstance;  
  42.     }  
  43.   
  44.     /** 
  45.      * @return The Volley Request queue, the queue will be created if it is null 
  46.      */  
  47.     public RequestQueue getRequestQueue() {  
  48.         // lazy initialize the request queue, the queue instance will be  
  49.         // created when it is accessed for the first time  
  50.         if (mRequestQueue == null) {  
  51.             // 1  
  52.             // 2  
  53.             synchronized (ApplicationController.class) {  
  54.                 if (mRequestQueue == null) {  
  55.                     mRequestQueue = Volley  
  56.                             .newRequestQueue(getApplicationContext());  
  57.                 }  
  58.             }  
  59.         }  
  60.         return mRequestQueue;  
  61.     }  
  62.   
  63.     /** 
  64.      * Adds the specified request to the global queue, if tag is specified then 
  65.      * it is used else Default TAG is used. 
  66.      *  
  67.      * @param req 
  68.      * @param tag 
  69.      */  
  70.     public <T> void addToRequestQueue(Request<T> req, String tag) {  
  71.         // set the default tag if tag is empty  
  72.         req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);  
  73.   
  74.         VolleyLog.d("Adding request to queue: %s", req.getUrl());  
  75.   
  76.         getRequestQueue().add(req);  
  77.     }  
  78.   
  79.     /** 
  80.      * Adds the specified request to the global queue using the Default TAG. 
  81.      *  
  82.      * @param req 
  83.      * @param tag 
  84.      */  
  85.     public <T> void addToRequestQueue(Request<T> req) {  
  86.         // set the default tag if tag is empty  
  87.         req.setTag(TAG);  
  88.   
  89.         getRequestQueue().add(req);  
  90.     }  
  91.   
  92.     /** 
  93.      * Cancels all pending requests by the specified TAG, it is important to 
  94.      * specify a TAG so that the pending/ongoing requests can be cancelled. 
  95.      *  
  96.      * @param tag 
  97.      */  
  98.     public void cancelPendingRequests(Object tag) {  
  99.         if (mRequestQueue != null) {  
  100.             mRequestQueue.cancelAll(tag);  
  101.         }  
  102.     }  
  103. }  
接下来就是Volley虽然给我们提供了很多不同的Request(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest),但是还是满足不了我们实战中的需求,我们实战开发中经常用到的是xml格式,Gson解析。

接下来我们来看看,如何自定义Request

XmlRequest:

[java] view plaincopy
  1. public class XMLRequest extends Request<XmlPullParser> {  
  2.   
  3.     private final Listener<XmlPullParser> mListener;  
  4.   
  5.     public XMLRequest(int method, String url, Listener<XmlPullParser> listener,  
  6.             ErrorListener errorListener) {  
  7.         super(method, url, errorListener);  
  8.         mListener = listener;  
  9.     }  
  10.   
  11.     public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {  
  12.         this(Method.GET, url, listener, errorListener);  
  13.     }  
  14.   
  15.     @Override  
  16.     protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {  
  17.         try {  
  18.             String xmlString = new String(response.data,  
  19.                     HttpHeaderParser.parseCharset(response.headers));  
  20.             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  
  21.             XmlPullParser xmlPullParser = factory.newPullParser();  
  22.             xmlPullParser.setInput(new StringReader(xmlString));  
  23.             return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));  
  24.         } catch (UnsupportedEncodingException e) {  
  25.             return Response.error(new ParseError(e));  
  26.         } catch (XmlPullParserException e) {  
  27.             return Response.error(new ParseError(e));  
  28.         }  
  29.     }  
  30.   
  31.     @Override  
  32.     protected void deliverResponse(XmlPullParser response) {  
  33.         mListener.onResponse(response);  
  34.     }  
  35.   
  36. }  

GsonRequest(注意需要自行导入gson.jar):

[java] view plaincopy
  1. public class GsonRequest<T> extends Request<T> {  
  2.   
  3.     private final Listener<T> mListener;  
  4.   
  5.     private Gson mGson;  
  6.   
  7.     private Class<T> mClass;  
  8.   
  9.     public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,  
  10.             ErrorListener errorListener) {  
  11.         super(method, url, errorListener);  
  12.         mGson = new Gson();  
  13.         mClass = clazz;  
  14.         mListener = listener;  
  15.     }  
  16.   
  17.     public GsonRequest(String url, Class<T> clazz, Listener<T> listener,  
  18.             ErrorListener errorListener) {  
  19.         this(Method.GET, url, clazz, listener, errorListener);  
  20.     }  
  21.   
  22.     @Override  
  23.     protected Response<T> parseNetworkResponse(NetworkResponse response) {  
  24.         try {  
  25.             String jsonString = new String(response.data,  
  26.                     HttpHeaderParser.parseCharset(response.headers));  
  27.             return Response.success(mGson.fromJson(jsonString, mClass),  
  28.                     HttpHeaderParser.parseCacheHeaders(response));  
  29.         } catch (UnsupportedEncodingException e) {  
  30.             return Response.error(new ParseError(e));  
  31.         }  
  32.     }  
  33.   
  34.     @Override  
  35.     protected void deliverResponse(T response) {  
  36.         mListener.onResponse(response);  
  37.     }  
  38.   
  39. }  
接下只差最后一步了就是封装它的错误处理,使用过volley的都知道,volley的监听错误提示都是NoConnectionError。。。等等,这类的错误提示,显然这不是我们想给用户呈现的错误提示,因为就算提示了用户也不明白什么意思,所以我们还要封装一下,能让用户看的更清楚的理解这些错误提示。ym—— Android网络框架Volley(体验篇)我们讲过每个请求都需要设置一个失败的监听:

[java] view plaincopy
  1. // 共用失败回调  
  2. private class StrErrListener implements ErrorListener {  
  3.   
  4.     @Override  
  5.     public void onErrorResponse(VolleyError arg0) {  
  6.         Toast.makeText(mContext,  
  7.                 VolleyErrorHelper.getMessage(arg0, mContext),  
  8.                 Toast.LENGTH_LONG).show();  
  9.     }  
  10.   
  11. }  
以上代码有个VolleyError对象,我们可以从这个对象上下手:

[java] view plaincopy
  1. package com.example.volley;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. import android.content.Context;  
  7.   
  8. import com.android.volley.AuthFailureError;  
  9. import com.android.volley.NetworkError;  
  10. import com.android.volley.NetworkResponse;  
  11. import com.android.volley.NoConnectionError;  
  12. import com.android.volley.ServerError;  
  13. import com.android.volley.TimeoutError;  
  14. import com.android.volley.VolleyError;  
  15. import com.google.gson.Gson;  
  16. import com.google.gson.reflect.TypeToken;  
  17. //正如前面代码看到的,在创建一个请求时,需要添加一个错误监听onErrorResponse。如果请求发生异常,会返回一个VolleyError实例。  
  18. //以下是Volley的异常列表:  
  19. //AuthFailureError:如果在做一个HTTP的身份验证,可能会发生这个错误。  
  20. //NetworkError:Socket关闭,服务器宕机,DNS错误都会产生这个错误。  
  21. //NoConnectionError:和NetworkError类似,这个是客户端没有网络连接。  
  22. //ParseError:在使用JsonObjectRequest或JsonArrayRequest时,如果接收到的JSON是畸形,会产生异常。  
  23. //SERVERERROR:服务器的响应的一个错误,最有可能的4xx或5xx HTTP状态代码。  
  24. //TimeoutError:Socket超时,服务器太忙或网络延迟会产生这个异常。默认情况下,Volley的超时时间为2.5秒。如果得到这个错误可以使用RetryPolicy。  
  25.   
  26. public class VolleyErrorHelper {  
  27.   
  28.     /** 
  29.      * Returns appropriate message which is to be displayed to the user against 
  30.      * the specified error object. 
  31.      *  
  32.      * @param error 
  33.      * @param context 
  34.      * @return 
  35.      */  
  36.     public static String getMessage(Object error, Context context) {  
  37.         if (error instanceof TimeoutError) {  
  38.             return context.getResources().getString(  
  39.                     R.string.generic_server_down);  
  40.         } else if (isServerProblem(error)) {  
  41.             return handleServerError(error, context);  
  42.         } else if (isNetworkProblem(error)) {  
  43.             return context.getResources().getString(R.string.no_internet);  
  44.         }  
  45.         return context.getResources().getString(R.string.generic_error);  
  46.     }  
  47.   
  48.     /** 
  49.      * Determines whether the error is related to network 
  50.      *  
  51.      * @param error 
  52.      * @return 
  53.      */  
  54.     private static boolean isNetworkProblem(Object error) {  
  55.         return (error instanceof NetworkError)  
  56.                 || (error instanceof NoConnectionError);  
  57.     }  
  58.   
  59.     /** 
  60.      * Determines whether the error is related to server 
  61.      *  
  62.      * @param error 
  63.      * @return 
  64.      */  
  65.     private static boolean isServerProblem(Object error) {  
  66.         return (error instanceof ServerError)  
  67.                 || (error instanceof AuthFailureError);  
  68.     }  
  69.   
  70.     /** 
  71.      * Handles the server error, tries to determine whether to show a stock 
  72.      * message or to show a message retrieved from the server. 
  73.      *  
  74.      * @param err 
  75.      * @param context 
  76.      * @return 
  77.      */  
  78.     private static String handleServerError(Object err, Context context) {  
  79.         VolleyError error = (VolleyError) err;  
  80.   
  81.         NetworkResponse response = error.networkResponse;  
  82.   
  83.         if (response != null) {  
  84.             switch (response.statusCode) {  
  85.             case 404:  
  86.             case 422:  
  87.             case 401:  
  88.                 try {  
  89.                     // server might return error like this { "error":  
  90.                     // "Some error occured" }  
  91.                     // Use "Gson" to parse the result  
  92.                     HashMap<String, String> result = new Gson().fromJson(  
  93.                             new String(response.data),  
  94.                             new TypeToken<Map<String, String>>() {  
  95.                             }.getType());  
  96.   
  97.                     if (result != null && result.containsKey("error")) {  
  98.                         return result.get("error");  
  99.                     }  
  100.   
  101.                 } catch (Exception e) {  
  102.                     e.printStackTrace();  
  103.                 }  
  104.                 // invalid request  
  105.                 return error.getMessage();  
  106.   
  107.             default:  
  108.                 return context.getResources().getString(  
  109.                         R.string.generic_server_down);  
  110.             }  
  111.         }  
  112.         return context.getResources().getString(R.string.generic_error);  
  113.     }  
  114. }  

以上代码中引用的xml是:

[html] view plaincopy
  1. <string name="no_internet">无网络连接~!</string>  
  2. <string name="generic_server_down">连接服务器失败~!</string>  
  3. <string name="generic_error">网络异常,请稍后再试~!</string>  
接下来,数据请求这一块已经说完了,我们来说下图片这一块,我个人喜欢使用universal-image-loader而不是volley自己提供的(个人认为使用起来universal-image-loader更便捷一些)。


下面主要是讲Volley在某些细节方面的选择和实现.值得我们学习的地方以及如果更好的使用Volley。


1.Volley本地缓存为什么有时候不会进行缓存?

缓存使用前提服务器必须支持,缓存,配置Cache-Control头信息,

因为Volley需要从这些头信息判断缓存是否已经过期。如果已经过期Volley将会重新从网络获取数据。

本人用抓包工具抓了无法缓存的返回头信息

可以支持缓存的头信息


2.如果我们自己写一个网络请求框架,我们内部实现会选择使用HttpURLConnection还是HttpClient?

我们通过源码来看看Volley是如何选择使用的

  

[java] view plaincopy
  1. public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
  2.     File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
  3.     String userAgent = "volley/0";  
  4.     try {  
  5.         String packageName = context.getPackageName();  
  6.         PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
  7.         userAgent = packageName + "/" + info.versionCode;  
  8.     } catch (NameNotFoundException e) {  
  9.     }  
  10.     if (stack == null) {  
  11.         if (Build.VERSION.SDK_INT >= 9) {  
  12.             stack = new HurlStack();  
  13.         } else {  
  14.             stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  15.         }  
  16.     }  
  17.     Network network = new BasicNetwork(stack);  
  18.     RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
  19.     queue.start();  
  20.     return queue;  
  21. }  
这里会判断如果手机系统版本号是大于9(Android 2.3)的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的,这里为什么这样选择呢?参考文章:Android访问网络,使用HttpURLConnection还是HttpClient?这就是它为何这么快的原因。

从这点我们可以学习到,要针对不同SDK版本做去相应更优的处理方式,这样才能达到最好的效果。


 3.Volley给我们提供了ImageRrequest,ImageLoader,NetworkImageView,它们分别使用于什么场景为什么?

单张图片的加载可以通过发起 ImageReuqst 请求来实现,但为了应用内存缓存,推荐使用 ImageLoader 

NetwoekImageView专门用于批量图片加载的场景:

[java] view plaincopy
  1. public class NetworkImageView extends ImageView {  
  2.     private String mUrl;  
  3.   
  4.     // 默认显示的图片  
  5.     private int mDefaultImageId;  
  6.   
  7.     // 加载失败时显示的图片  
  8.     private int mErrorImageId;  
  9.   
  10.     // 主方法入口  
  11.     public void setImageUrl(String url, ImageLoader imageLoader) {  
  12.         mUrl = url;  
  13.         mImageLoader = imageLoader;  
  14.         // 这个方法将会对ImageView的尺寸是否有效、是否为同一张图片进行判断  
  15.         // 在执行新请求前,也会取消上一次在这个View里启动的另一个已经失效的请求  
  16.         // 由于篇幅的限制以及代码行数太多,这里不贴出具体实现的代码  
  17.         loadImageIfNecessary(false);  
  18.     }  
  19.   
  20.     // 如果图片已经滑离屏幕,变为不可见,将执行取消请求的操作  
  21.     @Override  
  22.     protected void onDetachedFromWindow() {  
  23.         if (mImageContainer != null) mImageContainer.cancelRequest();  
  24.         super.onDetachedFromWindow();  
  25.     }  
  26. }  
在ListView加载多张图片的时候,NetworkImageView可以防止出现图片错误的现象,以及当NetworkImageView滑出屏幕的时候会取消加载图片请求,这样就保证加载多张图片的时候用户快速滑动列表的流畅性。给用户带来更优的体验。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果屏幕ic坏了怎么办 苹果6手机触摸屏失灵怎么办 苹果5s屏幕黑了怎么办 乐视手机充电慢怎么办 苹果5s触摸屏失灵怎么办 苹果6出现闪屏怎么办 苹果4s没有卡槽怎么办 苹果se触屏失灵怎么办 苹果6s屏幕乱跳怎么办 6s屏幕触摸失灵怎么办 苹果6sp屏幕失灵怎么办 苹果6s屏幕不动怎么办 苹果6s卡住了怎么办 苹果6s经常卡屏怎么办 苹果6s点不动了怎么办 手机屏自己乱点怎么办 苹果手机摔开了怎么办 苹果屏幕摔开了怎么办 苹果手机6开不了机怎么办 苹果6开不开机怎么办 苹果6s开不开机怎么办 苹果6手机开不了机怎么办 苹果6s不能开机怎么办 苹果6p无法开机怎么办 苹果6s开不了机怎么办 苹果7屏幕划不动怎么办 苹果6老是卡机怎么办 苹果手机黑屏开不了机怎么办 苹果6plus掉水里了怎么办 苹果6黑屏开不了机怎么办 苹果手机6死机了怎么办 苹果7手机死机怎么办啊 苹果7突然死机了怎么办 苹果手机5s死机怎么办 苹果六s死机了怎么办 苹果4卡机了怎么办 苹果4锁死了怎么办啊 苹果4手机停机了怎么办 苹果4开不了机怎么办 苹果4开不了机怎么办啊 苹果7手机死机了怎么办