Android组件Volley学习

来源:互联网 发布:数据挖掘产品 编辑:程序博客网 时间:2024/06/07 20:00

1. 项目地址本地下载

2. 参考博文

http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/

http://blog.csdn.net/guolin_blog/article/details/17482095

Volley是Google提供的网络通信库,通过它可以使得从网络获取数据更加的方便。它对网络的请求做了很多封装优化,让开发者更加便捷的开发。在新的Android文档中,已经加入了Volley方面的部分,在Training部分。网上有很多对它的参考,当然,Android文档就是一份很好的参考。大牛都批评只会使用而不去研究它源码的码农,但,我还是先浅尝辄止,不去学习的它的源码。不过,他们都推荐它的源码写得非常好,那当然要适时的找时间学习它的源码。下面记录下它的使用方法,涉及到的有异步请求文本,json,图片等请求,Volley是对返回的字节流做了封装。当然,我们也可以根据需要继承Request,定制自己的Request。另外,需要记录的是内存级别和硬盘(sd卡)的缓存处理。

我们首先要获得一个RequestQueue对象,文档推荐使用单例模式。推荐的方法是实现一个单模式类,提供一个获取对象的单例方法。如下:

    public RequestQueue getRequestQueue() {        if (mRequestQueue == null) {            // getApplicationContext() is key, it keeps you from leaking the            // Activity or BroadcastReceiver if someone passes one in.            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());        }        return mRequestQueue;    }

其中,Volley#newRequestQueue(Context context) 这个静态方法的参数很讲究。不推荐使用Activity的context,而是使用Application的context,正如上面的代码所示。这样,即使一个Activity销毁了,RequestQueue对象也不会跟随它销毁,而是和这个APP的生命周期相关联,避免了重复的生成。

接着,我们来看请求文本数据,如下:

StringRequest stringRequest = new StringRequest(Request.Method.GET, url,            new Response.Listener() {    @Override    public void onResponse(String response) {        // Display the first 500 characters of the response string.        mTextView.setText("Response is: "+ response.substring(0,500));    }}, new Response.ErrorListener() {    @Override    public void onErrorResponse(VolleyError error) {        mTextView.setText("That didn't work!");    }});

StringRequest有多个重载的构造方法,可以使用GET方式和POST方式,相应的有请求成功和请求失败的回调监听函数。那么对于POST方式,如何传递参数呢?在JsonObjectRequest的构造方法中,有个参数选项可以传进一个Map结构的键值对参数。而StringRequest 中确没有,参考博文的方法是,重写StringRequest的getParams()方法,如下:

StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {@Overrideprotected Map<String, String> getParams() throws AuthFailureError {Map<String, String> map = new HashMap<String, String>();map.put("params1", "value1");map.put("params2", "value2");return map;}};

有了请求对象后,就把该对象加入的请求队列RequestQueue中即可。

// Add the request to the RequestQueue.queue.add(stringRequest);


如何请求JSON数据呢?就是构造一个JsonObjectRequest或者JsonArrayRequest请求对象,形式和上面大同小异。然后加入到请求队列中即可。

那如何,请求图片资源呢?记录这两种好用的方式。一种是构造一个ImageRequest对象,然后在请求成功的回调函数中ImageView#setImageBitmap 即可。构造方法中可以指定图片的大小。

ImageRequest imageRequest = new ImageRequest(imgUrl1, new Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {mImageView1.setImageBitmap(response);}}, 200, 200, Config.ARGB_8888, new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {Log.e(TAG, "imageRequest-->"+error.getMessage());}});mQueue.add(imageRequest);

和上面的类似。另一种功能强大一些,它可以设定加载中的图片,加载失败的图片,和内存缓存的策略。该方法使用的是ImageLoader类实现,首先生成一个该对象:

mImageLoader = new ImageLoader(mQueue, new LruBitmapCache(this));

第一个参数是请求队列,第二个参数是缓存策略。文档中提供一个例子。当然,我们可以自己实现或者实现为不使用缓存。最后,我们调用一下#get方法就可以了,

mImageLoader.get(imgUrl2, ImageLoader.getImageListener(mImageView2, android.R.drawable.alert_dark_frame, android.R.drawable.edit_text));

内存缓存中,有个奇怪的事,我在文档提供的 LruBitmapCache类的 #getBitmap 方法中,加入log观察,发现多次请求同一个url图片时,它的 #get 方法返回的对象返回为null,根本就没有缓存到内存中。


基本的网络请求,就如上。有时,我们需要设置一下HTTP头,该如何做?覆盖请求对象的 #getHeaders 方法即可,

       @Override       public Map<String, String> getHeaders() throws AuthFailureError {           HashMap<String, String> headers = new HashMap<String, String>();           headers.put("CUSTOM_HEADER", "Yahoo");           headers.put("ANOTHER_CUSTOM_HEADER", "Google");           return headers;       }

有时,当我们退出Activity时,希望取消正在进行的网络操作,我们可以调用RequestQueue的一系列的 #cancel* 方法,比如,

public static final String TAG = "MyTag";StringRequest stringRequest; // Assume this exists.RequestQueue mRequestQueue;  // Assume this exists.// Set the tag on the request.stringRequest.setTag(TAG);// Add the request to the RequestQueue.mRequestQueue.add(stringRequest);

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

有时,我们需要根据自己的需求封装请求返回的格式,那么我们可以继承Request来实现一个,文档中给出一个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;    /**     * Make a GET request and return a parsed object from JSON.     *     * @param url URL of the request to make     * @param clazz Relevant class object, for Gson's reflection     * @param headers Map of request headers     */    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    public Map<String, String> getHeaders() throws AuthFailureError {        return headers != null ? headers : super.getHeaders();    }    @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));        }    }}

现在,还有一个较重要的问题要记录,就是缓存问题。Volley默认是对每一个url 都缓存到sd卡中(实验得出)。默认大小是5M。那么我们如何取消缓存或者修改缓存大小呢?修改缓存大小,可以如下(参考文档),

RequestQueue mRequestQueue;// Instantiate the cacheCache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap// Set up the network to use HttpURLConnection as the HTTP client.Network network = new BasicNetwork(new HurlStack());// Instantiate the RequestQueue with the cache and network.mRequestQueue = new RequestQueue(cache, network);// Start the queuemRequestQueue.start();String url ="http://www.myurl.com";// Formulate the request and handle the response.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    }});// Add the request to the RequestQueue.mRequestQueue.add(stringRequest);...

那么,如何设置不进行缓存呢,暂时没有发现直接的方法,但阅读源码,可以很快的实现,方法如下,

/** * 没有硬盘缓存 * @param context * @param stack * @return */    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {    String userAgent = "volley/0";        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                stack = new HurlStack();            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        Network network = new BasicNetwork(stack);        RequestQueue queue = new RequestQueue(new NoCache(), network);        queue.start();        return queue;    }

和原来代码中不同的是,删除了一些缓存初始化的代码,以及构造RequestQueue请求队列时,传入的是 NoCache对象,这样即可。我们也可以通过RequestQueue#getCache方法获得Cache对象,然后进行移除指定key的缓存文件,或者clear 清空全部。这样,功能就达到了组件Android-Universal-Image-Loader所能实现的。



1 0