本文翻译自android官方文档,结合自己测试,整理如下。

使用Volley进行网络通信

Volley是Android官方提出的HTTP网络通信框架,它能够使得Android程序网络传输更容易而且更快。

Volley有以下优点:

  • 自动调度网络请求。
  • 多并发网络连接。
  • 标准的HTTP网络请求缓存。
  • 支持请求优先级。
  • 取消请求API。我们可以取消单次请求,也可以取消请求块或一个范围。
  • 容易定制。
  • 很好地排序,能够使得网络异步数据正确地填充UI。
  • 调试与追踪工具。

Volley不适合大数据量传输(包括上传和下载)或者数据流操作,这是因为Volley在内存中处理所有的请求。对于大数据量传输可以考虑使用其它工具,例如DownloadManager。

在使用Volley前要导入jar包,具体办法自行解决,哈哈,,,,。

发送一个简单的请求

我们通过创建一个RequestQueue和传递给Volley一个Request对象使用Volley。RequestQueue管理工作线程,这些线程用于网络操作/读写缓存/解析请求等。Requests处理原始请求,Volley把解析结果传递给主线程。

本节中将介绍如何使用Volley.newRequestQueue()发送请求,添加请求,以及取消请求。

添加INTERNET许可

为了使用Volley,我们必须在manifest文件中添加许可android.permission.INTERNET,没有该请求的话我们无法使用网络。

使用newRequestQueue()

可以通过Volley.newRequestQueue()获取一个RequestQueue对象,然后添加一个Request到该请求队列中,这里我们使用StringRequest。具体步骤如下:

  1. 获取请求队列RequestQueue对象;
  2. 创建StringRequest对象;
  3. 将请求对象添加到请求队列中。

代码如下:

public void sendRequestWithVolley(View view){        // 1. 创建请求队列        RequestQueue queue = Volley.newRequestQueue(this);        // 2. 创建请求对象        // 2.1 创建url        String url = "http://blog.csdn.net/wangyongge85";        // 2.2 创建请求对象,这里使用StringRequest        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,                new Response.Listener<String>() {                    @Override                    public void onResponse(String response) {                        Log.d(TAG,response);                    }                },                new Response.ErrorListener() {                    @Override                    public void onErrorResponse(VolleyError error) {                        Log.d(TAG,error.getMessage());                    }                });        // 3. 将请求对象加入到请求队列中        queue.add(stringRequest);    }

上述代码中,我们创建了一个StringRequest对象,StringRequest的构造器接收四个参数,分别为:

  • HTTP请求方法;
  • 目标服务器的URL;
  • 服务器响应成功回调方法;
  • 服务器响应失败回调方法。

由于Response.Listener和Response.ErrorListener都是函数式接口(只有一个抽象方法),因此可以使用Lambda表达式取代。例如:Response.Listener<String>可以替换为:(String response)->{Log.d(TAG,s);}

通过上述三步我们就完成了HTTP请求。

这里我们使用了Request.Method.GET方法,若要向服务器发送数据,则使用Request.Method.POST。但是StringRequest中并没有直接提供设置传递数据参数的构造器,我们只能通过重写父类的getParams()方法来传递提交的数据,如下:

 StringRequest stringRequest2 = new StringRequest(Request.Method.POST, url,               (String s)->{Log.d(TAG,s);},                (VolleyError volleyError)-> {Log.d(TAG,volleyError.getMessage());        })        {            @Override            protected Map<String, String> getParams() throws AuthFailureError {                Map<String, String> map = super.getParams();                map.put("param","value");                return map;            }        };

Volley总是在main线程中处理响应。将接收的数据在主线程中填充UI是很方便的,可以直接使用响应结果更新UI。

发送请求

为了发送一次请求,我们可以简单的通过RequestQueue的add()方法,例如上面代码中。一旦我们添加过请求,该请求就会移动到请求队列中,并处理,然后返回原始响应数据并传输到Response.Listener或Response.ErrorListener处理。

当我们调用add()方法时,Volley将运行一个高速缓存处理线程和一个网络调度线程池。若请求在缓存中的话,缓存响应就将在缓存线程中解析,并将解析响应传递给main线程;若请求不在缓存中的话,该请求将加入到网络请求队列中。第一个可用的网络线程从队列中取请求,执行HTTP事务,在子线程中解析响应,将响应写入缓存中,然后将解析的响应返回给主线程中。

注意到耗时的操作如阻塞I/O和解析解码等都要在子线程中执行。我们可以在任何线程中添加请求,但是处理响应结果总是在main线程中。

下图描述了请求全过程:

取消一次请求

为了取消一次请求,可以调用Request对象的cancel()方法。一旦被取消,Volley能够保证我们的响应请求绝对不会被调用。这意味着在实际中我们能够在activity的onStop()中取消所有的延迟请求,我们不需要通过检查是否getActivity()==null,是否onSaveInstanceState()已经被调用,或者其它内容回收响应处理者。

为了能够在合适的时间取消请求,我们可以在每次请求时设置一个标签,通过该标签我们能够取消相应的请求。例如下面的代码:

public static final String TAG = "MyTag";StringRequest stringRequest;RequestQueue mRequestQueue;// 设置请求标签stringRequest.setTag(TAG);// 将请求添加到请求队列中mRequestQueue.add(stringRequest);

然后我们可以在Activity中的onStop()方法取消所有标签为TAG的请求:

@Overrideprotected void onStop () {    super.onStop();    if (mRequestQueue != null) {        mRequestQueue.cancelAll(TAG);    }}

设置RequestQueue请求队列

上一节讲解了如何使用Volley.newRequestQueue()设置RequestQueue对象,并充分利用Volley默认的请求方法。这一节中我们将通过下面的方法创建RequestQueue队列,方便我们实现自定义的请求方法。下面我们将使用单例模式创建一个RequestQueue对象,这种方式使得该RequestQueue对象的生存周期和我们程序的生存周期一样。

设置网络和缓存

RequestQueue需要完成下列两件事:执行传输请求的网络Network和处理缓存的空间Cache。在Volley中的toolbox包中有默认的实现:DiskBasedCache和BasicNetwork。DiskBasedCache提供一个文件缓存,并且带有内存索引;BasicNetwork提供基于HttpURLConnection或者AndroidHttpClient的网络传输请求,是Volley默认的网络请求实现。

- 在低于API 9 (Gingerbread)中使用的是AndroidHttpClient,在API 9之前,HttpURLConnection是不可靠的。- 在高于API 9之后(包括API 9),使用HttpURLConnection。

为了应用程序能够在Android所有的版本中使用Volley,我们可以检查android设备的版本,选择合适的HTTP客户端来创建BasicNetwork对象,例如:

HttpStack stack;if (stack == null) {    if (Build.VERSION.SDK_INT >= 9) {        stack = new HurlStack();    } else {        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));    }}Network network = new BasicNetwork(stack);

在上面的例子中,通过HttpStack对象创建了一个BasicNetwork对象。而对于HttpStack对象来说在API 9之前是AndroidHttpClient(实现了HttpStack接口),在API 9之后是HurlStack对象(实现了HttpStack接口,并且内部使用HttpURLConnection实现网络请求)。

下面的代码描述了通过DiskBasedCache对象和BasicNetwork对象创建RequestQueue对象:

RequestQueue mRequestQueue;// 实例化缓存Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap// 使用HttpURLConnection作为Http客户端连接方式Network network = new BasicNetwork(new HurlStack());// 使用cache和network实例化消息队列mRequestQueue = new RequestQueue(cache, network);// 启动消息队列mRequestQueue.start();String url ="http://blog.csdn.net/wangyongge85";// 实例化请求对象StringRequest stringRequest = new StringRequest(Request.Method.GET, url,        new Response.Listener<String>() {    @Override    public void onResponse(String response) {        // Do something with the response    }},    new Response.ErrorListener() {        @Override        public void onErrorResponse(VolleyError error) {            // Handle error    }});// 将消息添加到消息队列中mRequestQueue.add(stringRequest);...

若我们仅仅只请求一次,不想使用线程池,我们可以在任何需要它的地方创建RequestQueue类。但是更常用的做法是创建一个全局RequestQueue单例对象。下面详细描述这种方法。

使用单例模式

若我们的程序一直使用网络的话,最高效的做法是创建一个全局的RequestQueue单例对象。实现单例的方法有很多种,本节中推荐使用一个单例封装RequestQueue对象和其他Volley方法。不建议在Application子类中的onCreate()中创建RequestQueue对象。

在实例化RequestQueue时,必须使用Application作为参数,而不能使用Activity作为参数,这种方式保证RequestQueue在整个程序运行期间都存在,而不是当activity重建时RequestQueue也重建(例如用户旋转手机屏幕)。并且能够防止内存泄漏。

如下单例模式封装了RequestQueue和ImageLoader:

private static MySingleton mInstance;private RequestQueue mRequestQueue;private ImageLoader mImageLoader;private static Context mCtx;private MySingleton(Context context) {    mCtx = context;    // 实例化消息队列    mRequestQueue = getRequestQueue();    // 实例化ImageLoader,这个在下面部分有详细讲解    mImageLoader = new ImageLoader(mRequestQueue,            new ImageLoader.ImageCache() {        private final LruCache<String, Bitmap>                cache = new LruCache<String, Bitmap>(20);        @Override        public Bitmap getBitmap(String url) {            return cache.get(url);        }        @Override        public void putBitmap(String url, Bitmap bitmap) {            cache.put(url, bitmap);        }    });}public static synchronized MySingleton getInstance(Context context) {    if (mInstance == null) {        mInstance = new MySingleton(context);    }    return mInstance;}/** * 实例化消息队列方法 **/public RequestQueue getRequestQueue() {    if (mRequestQueue == null) {        // getApplicationContext()能够防止内存泄漏        mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());    }    return mRequestQueue;}public void addToRequestQueue(Request<T> req) {    getRequestQueue().add(req);}public ImageLoader getImageLoader() {    return mImageLoader;}

下面代码给出了如何使用该单例模式:

// 获取请求队列RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).    getRequestQueue();...// 将请求对象发送到请求队列中MySingleton.getInstance(this).addToRequestQueue(stringRequest);

制定标准请求

本节中将介绍如何使用Volley默认实现的各种请求:

  • StringRequest
    须指定URL,接收未处理的字符串响应。
  • ImageRequest
    须指定URL,接收图片响应。
  • JsonObjectRequestJsonArrayRequest
    都是JsonRequest的子类。指定URL,各自接收Json对象或Json数组响应。

若我们想接收以上数据响应的话,可以不用我们自定义请求,直接使用以上类就可以。

本章节将描述如何使用这些标准的请求方式。

请求图片响应

Volley提供了下列的类来请求图片。这些类之间存在着相互关系,提供不同级别的支持处理图像:

  • ImageRequest从给定的URL中获得图片,并且通过解析的bitmap回调。它也能够提供一些方便的方法,例如指定一个大小来重新设置图片等。主要优点在于Volley线程处理机制能够保证耗时的图片操作(解码,重新绘画)自动在子线程中执行。
  • ImageLoader
    帮助类,能够加载和缓存从远程URL获取的图片。ImageLoader是一个管理大量的ImageRequest的类。
  • MetworkImageView
    建立在ImageLoader上,能够高效地在合适的地方显示通过网络获取的图片,因此可以替换ImageView。若该控件不再可用时,它也能够取消延迟请求。

使用ImageRequest

下面是一个简单的使用ImageRequest的例子。它能够检索指定URL中的图片,然后显示在我们的程序中。注意下面使用到的RequestQueue是通过单例模式创建的:

ImageView mImageView;String url = "http://i.imgur.com/7spzG.png";mImageView = (ImageView) findViewById(R.id.myImage);...// 检索URL指定位置的图片,然后显示在UI上。ImageRequest request = new ImageRequest(url,    new Response.Listener() {        @Override        public void onResponse(Bitmap bitmap) {            mImageView.setImageBitmap(bitmap);        }    }, 0, 0, null,    new Response.ErrorListener() {        public void onErrorResponse(VolleyError error) {            mImageView.setImageResource(R.drawable.image_load_error);        }    });// 通过单例获取请求队列,然后将请求加入到请求队列中MySingleton.getInstance(this).addToRequestQueue(request);

ImageRequest的构造器接收六个参数,分别为:

  1. 图片的URL;
  2. 请求成功的回调;
  3. 指定允许图片最大的宽度,大于指定值则会压缩,0的话表示按原图大小显示;
  4. 指定允许图片最大的高度,大于指定值则会压缩,0的话表示按原图大小显示;
  5. 指定图片的颜色属性;
  6. 请求失败的回调。

使用ImageLoader和NetworkImageView

我们可以使用ImageLoader和NetworkImageView来高效地显示多个图片,例如在ListView显示。在我们的布局文件中使用NetworkImageView(和ImageView差不多)。

使用ImageLoader

使用ImageLoader将图片显示在ImageView中,例如:

ImageView mImageView;// 加载指定图片private static final String IMAGE_URL =    "http://i.imgur.com/7spzG.png";...mImageView = (ImageView) findViewById(R.id.regularImageView);// 实例化ImageLoader,这个在下面部分有详细讲解mImageLoader = new ImageLoader(mRequestQueue,new ImageLoader.ImageCache() {        private final LruCache<String, Bitmap> cache = new LruCache<String, Bitmap>(20);        @Override        public Bitmap getBitmap(String url) {            return cache.get(url);        }        @Override        public void putBitmap(String url, Bitmap bitmap) {            cache.put(url, bitmap);        }    });mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,         R.drawable.def_image, R.drawable.err_image));

ImageLoader构造器参数为:

  • 请求队列对象;
  • ImageCache对象,用于缓存图片。

创建ImageLoader对象之后,调用其get()方法加载图片,get()方法接收两个参数:

  • 图片的URL地址;
  • ImageListener对象,用于监听图片加载情况,可以通过ImageLoader.getImageListener()获得,该方法接收三个参数:

    • 显示图片的ImageView;
    • 加载图片时显示的图片;
    • 加载图片失败后显示的图片。

使用NetworkImageView

若要使用NetworkImageView的话,首先在布局文件中设置NetworkImageView,代码如下:

<com.android.volley.toolbox.NetworkImageView  android:id="@+id/networkImageView" android:layout_width="150dp" android:layout_height="170dp" android:layout_centerHorizontal="true" />

然后使用ImageLoader将图片显示,代码如下:

ImageLoader mImageLoader;NetworkImageView mNetworkImageView;private static final String IMAGE_URL = "http://i.imgur.com/7spzG.png";...// 获取NetworkImageViewmNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);// 通过单例模式获取ImageLoadermImageLoader = MySingleton.getInstance(this).getImageLoader();// 默认显示图片networkImageView.setDefaultImageResId(R.drawable.default_image);// 加载失败后显示的图片networkImageView.setErrorImageResId(R.drawable.failed_image);// 设置将要加载的图片,并且指定ImageLoader处理请求mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);

使用单例模式能够允许bitmap缓存超出activity的生命周期。若我们在activity中创建了一个ImageLoader,当用户每次旋转设备时,activity都需要重新创建,ImageLoader也会被重新创建。使用单例的话就会避免这种反复重建。

LRU缓存例子

Volley通过DiskBasedCache类提供了一个标准的缓存实现。该类直接将文件缓存在指定的存储器目录上。但是为了使用ImageLoader,我们应该提供一个自定义的内存LRU bitmap缓存,该缓存实现了ImageLoader.ImageCache接口。同样该缓存也可以设置成单例模式。

简单代码如下:

import android.graphics.Bitmap;import android.support.v4.util.LruCache;import android.util.DisplayMetrics;import com.android.volley.toolbox.ImageLoader.ImageCache;public class LruBitmapCache extends LruCache<String, Bitmap> implements ImageCache {    public LruBitmapCache(int maxSize) {        super(maxSize);    }    public LruBitmapCache(Context ctx) {        this(getCacheSize(ctx));    }    @Override    protected int sizeOf(String key, Bitmap value) {        return value.getRowBytes() * value.getHeight();    }    @Override    public Bitmap getBitmap(String url) {        return get(url);    }    @Override    public void putBitmap(String url, Bitmap bitmap) {        put(url, bitmap);    }    // Returns a cache size equal to approximately three screens worth of images.    public static int getCacheSize(Context ctx) {        final DisplayMetrics displayMetrics = ctx.getResources().                getDisplayMetrics();        final int screenWidth = displayMetrics.widthPixels;        final int screenHeight = displayMetrics.heightPixels;        // 4 bytes per pixel        final int screenBytes = screenWidth * screenHeight * 4;        return screenBytes * 3;    }}

下面是通过cache实例化ImageLoader的例子:

RequestQueue mRequestQueue;ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(            LruBitmapCache.getCacheSize()));

请求JSON

Volley提供了下列方法处理JSON请求:

  • JsonArrayRequest
    从给定的URL中检索JSONArray响应体。
  • JsonObjectRequest
    从给定的URL中检索JSON对象响应体。允许添加额外的JSONObject作为请求体的一部分。

这两个类都是基于基类JsonRequest的实现。我们可以根据下面相同的基本模式使用他们:

TextView mTxtDisplay;ImageView mImageView;mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);String url = "http://my-json-feed";JsonObjectRequest jsObjRequest = new JsonObjectRequest        (Request.Method.GET, url, null, new Response.Listener() {            @Override            public void onResponse(JSONObject response) {                mTxtDisplay.setText("Response: " + response.toString());            }        }, new Response.ErrorListener() {            @Override            public void onErrorResponse(VolleyError error) {            }        });MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

其中第三个参数为请求体,这里为null。

实现自定义请求

本节中将描述如何实现自定义的请求。

编写自定义请求

大部分请求都可以直接使用Volley提供的API,如:字符串,图片或者JSON。

如我们想实现自定义的请求如:GSON和XML。可以通过下列方法:

  1. 继承Request<T>类,其中T是解析响应的类型。
  2. 实现抽象方法parseNetworkResponse()和deliverResponse()`。

下面详细讲解具体实现方法。

实现parseNetworkResponse()

Response类封装了传递的解析的响应。如下实现代码:

@Overrideprotected Response<T> parseNetworkResponse(        NetworkResponse response) {    try {        String json = new String(response.data,        HttpHeaderParser.parseCharset(response.headers));    return Response.success(gson.fromJson(json, clazz),    HttpHeaderParser.parseCacheHeaders(response));    }    // handle errors...}

注意

  • parseNetworkResponse()接收一个NetworkResponse参数,包括响应加载字节数组,HTTP,响应头。
  • 必须返回Response,该对象包含了我们定义的响应对象和数据缓存或者错误。

若我们的协议不包括标准的缓存语义,我们应该建立Cache.Entry,但是大多数请求都可以使用下面的方法:

return Response.success(myDecodedObject,        HttpHeaderParser.parseCacheHeaders(response));

Volley在子线程中调用parseNetworkPesponse()。这样能够确保耗时操作(解析JPEG成Bitmap等操作)不会阻塞UI线程。

deliverResponse()

Volley能够在主线程中处理parseNetworkResponse()返回的结果。大多数采用下述方法:

protected void deliverResponse(T response) {        listener.onResponse(response);

parseNetworkResponse()方法中,先将服务器响应的数据解析,然后在deliverResponse()方法中进行回调。

下面以Gson请求为例。

自定义Gson请求

下面我们实现自定义Gson请求,代码如下:

public class GsonRequest<T> extends Request<T> {    private final Gson gson = new Gson();    private final Class<T> clazz;    private final Map<String, String> headers;    private final Listener<T> listener;    /** * GET请求方式,并且返回JSON解析对象 */    public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,            Listener<T> listener, ErrorListener errorListener) {        super(Method.GET, url, errorListener);        this.clazz = clazz;        this.headers = headers;        this.listener = listener;    }    /** * 方法回调 */    @Override    protected void deliverResponse(T response) {        listener.onResponse(response);    }    @Override    protected Response<T> parseNetworkResponse(NetworkResponse response) {        try {            String json = new String(                    response.data,                    HttpHeaderParser.parseCharset(response.headers));            return Response.success(                    gson.fromJson(json, clazz),                    HttpHeaderParser.parseCacheHeaders(response));        } catch (UnsupportedEncodingException e) {            return Response.error(new ParseError(e));        } catch (JsonSyntaxException e) {            return Response.error(new ParseError(e));        }    }    @Override    public Map<String, String> getHeaders() throws AuthFailureError {        return headers != null ? headers : super.getHeaders();    }}

更多相关文章

  1. android 消息机制浅析
  2. Android进程通信(IPC)之AIDL对象传递
  3. 解析Android中的线程
  4. Android四大图片缓存框架之-Fresco(一)
  5. Android(安卓): 录音实现之AudioRecord类
  6. Android(安卓)内存泄漏总结
  7. Android(安卓)AsyncTask使用以及源码解析
  8. Android基础入门教程——4.2.1 Service初涉
  9. 【Android】音乐播放器边播边缓存(三)AndroidVideoCache的先下载再

随机推荐

  1. android之wifi移植全过程(一)
  2. Android资源下载
  3. 2011.07.08(2)——— android 背景模糊
  4. Android 使用Telephony API
  5. android用于打开各种文件的intent
  6. Android : 继承BaseAdapter对ListView进
  7. 强制系统横屏竖屏
  8. Android: 下载并编译Android Source Tree
  9. iPhone与Android对比
  10. 2011.08.30——— android setOnTouchLis