zoukankan      html  css  js  c++  java
  • Volley解析(一)--Volley的使用

    Volley解析(一)--Volley的使用

    Volley 是一个HTTP协议的网络请求框架

    Volley的优势:

    1. 自动安排网络请求
    2. 支持多个并发网络连接
    3. 具有标准HTTP缓存一致性的透明磁盘和内存响应缓存
    4. 支持请求优先级
    5. 支持取消请求api。可以取消单个请求,也可以设置要取消的请求的块或范围。
    6. 定制方便,支持失败重试和回退
    7. 强排序,可以轻松地从网络异步获取的数据中正确填充UI。
    8. 具有调试和跟踪工具

    它易于与任何协议集成,支持原始字符串、图像和JSON的解析。通过提供你需要的功能的内置支持,Volley使你从写样板代码脱身,可以让你专注于特定于应用程序的逻辑。

    Volley的优势劣势都在于使用内存缓存!!

    Volley擅长的场景:

    Volley擅长用于填充UI的RPC类型操作,如将搜索结果页作为结构化数据抓取。适合短小,频繁的请求。因为解析过程结果会保留在内存。

    Volley不擅长的场景:

    Volley不适合大型下载或流媒体操作,因为在解析过程中保留了内存中的所有响应。

    使用newRequestQueue简单发送请求

    三步完成发送请求:

    1. 初始化请求队列
    2. 构造请求对象
    3. 请求对象加入请求队列

    Volley提供了一个方便的方法volley.newrequestqueue为用户配置一个请求队列.设置使用默认值,并启动队列。

    final TextView mTextView = (TextView) findViewById(R.id.text);
    ...
    
    // Instantiate the RequestQueue. 初始化请求队列
    RequestQueue queue = Volley.newRequestQueue(this);
    String url ="http://www.google.com";
    
    // Request a string response from the provided URL.构造请求对象
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            // Display the first 500 characters of the response string.
            mTextView.setText("Response is: "+ response.substring(0,500));
    		//UI线程
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            mTextView.setText("That didn't work!");
    		//UI线程
        }
    });
    // Add the request to the RequestQueue.
    queue.add(stringRequest);
    

    Volley总是在主线上传递解析的响应。在主线程中运行可以方便与接收的数据填充的UI控件,你可以直接从响应随意修改UI控件。但是它对库提供的许多重要语义尤其重要,特别是与取消请求相关的。
    一旦添加请求,它就通过管道移动,得到服务,并对其原始响应进行解析。

    当调用add,Volley就会跑一个cache线程和一个网络分发线程池(默认是4个线程的线程池)。
    当你添加一个请求队列,它会进缓存的线程:

    如果请求可以从高速缓存中得到服务,缓存响应将在高速缓存线程上解析,解析后的响应将在主线程上传递。

    如果请求不能从缓存中进行服务,那么它就被放置在网络队列中。第一个可用的网络线程接收来自队列的请求,执行HTTP事务,解析工作线程上的响应,将响应写入高速缓存,并将解析后的响应返回到主线程进行传输。

    注意,诸如阻塞I/O和解析/解码等昂贵的操作是在工作线程上完成的。可以从任何线程添加请求,但响应总是在主线程上传递。

    取消一个请求

    这种场景是比较常见的。比如在界面销毁之前,请求就需要取消。

    1 最简单的一个方式就是在请求的对象上直接调cancel()方法:

    stringRequest.cancel();
    

    一旦取消,Volley保证的响应Response处理程序将永远不会被调用。这意味着你可以取消你所有的请求在onstop()方法。而且在响应处理方法里不用检查getActivity() == null,也不用关心onSaveInstanceState()有没有被调用,或者去有意其他的一些防御性代码。

    这样带来的好处是巨大的,意味着Rrsponse中的任何操作在cancel以后不会执行,那就不会有意外发生。

    要利用这种行为,通常需要跟踪所有正在执行的请求,以便在适当的时候取消它们。

    有一个更简单的方法:可以将标记对象与每个请求关联起来。然后可以使用此标记来提供取消请求的范围。

    可以将所有请求标记为正在进行的界面。然后再onStop中调requestQueue.cancelAll(this)。同样,你也可以用tag标记把所有的缩略图图片请求和ViewPager中各个相关的tab页关联起来。在切换的时候就取消前一个tab的请求们

    2 批量范围取消请求
    //先标记
    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);
    
    //再在onStop方法中取消
    @Override
    protected void onStop () {
        super.onStop();
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(TAG);
        }
    }
    

    注意:要谨慎嗲用cancel方法.假设这样一个场景:你要根据请求的返回结果做下一步工作。而这步的工作在成功或者失败的时候都要执行。那如果取消掉了请求。这段必须执行的逻辑就永远执行不到了。

    再次说明:cancel后 ,response处理程序将不会被调用

    脱离newRequestQueue自己配置一个请求队列

    Volley.newRequestQueue 简化了请求队列的构造。但如果要自定义请求队列就要自己配置。一个网络请求队列需要两个工具:网络执行请求的运输,和缓存处理缓存。

    Volley toolbox有标准的实现。diskbasedcache采用文件/响应缓存一一对应的方式提供一个内存索引。

    BasicNetwork提供了一个基于首选的HTTP客户端的网络传输。默认是HttpUrlConnection API9一下是HttpClient

    RequestQueue mRequestQueue;
    
    // Instantiate the cache
    Cache 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 queue
    mRequestQueue.start();
    
    String url ="http://www.example.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);
    
    // ...
    

    如果支持做一次性的请求操作那用Volley.newRequestQueue就行了。然后再相应返回或者错误以后调用RequestQueue的stop把请求的线程池停掉就行了。

    但是通常情况的场景是在app的整个运行期内都要维持这个队列,把RequestQueue作为一个单例用。

    单例模式的RequestQueue

    最直接的方式就是在Application的onCreate中构建RequestQueue对象。但是这样做并不是最好的。

    最好的方式就是用静态单例更模块化的方式提供相同的功能

    一个关键的概念是,所以必须实例化时使用ApplicationContext,不是一个ActivityContext。这样是为了保证RequestQueue的生命周期和app存货的时间是一样的。

    	public class MySingleton {
        private static MySingleton mInstance;
        private RequestQueue mRequestQueue;
        private ImageLoader mImageLoader;
        private static Context mCtx;
    
        private MySingleton(Context context) {
            mCtx = context;
            mRequestQueue = getRequestQueue();
    
            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() is key, it keeps you from leaking the
                // Activity or BroadcastReceiver if someone passes one in.
                mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
            }
            return mRequestQueue;
        }
    
        public <T> void addToRequestQueue(Request<T> req) {
            getRequestQueue().add(req);
        }
    
        public ImageLoader getImageLoader() {
            return mImageLoader;
        }
    }
    

    这个单例中发送请求的用法:

    // Get a RequestQueue
    RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
        getRequestQueue();
    
    // ...
    
    // Add a request (in this example, called stringRequest) to your RequestQueue.
    MySingleton.getInstance(this).addToRequestQueue(stringRequest);
    

    实现自定义的请求对象

    大多数请求已经准备好在工具箱中使用实现;如果响应是字符串、图像或JSON,则可能不需要实现自定义请求。

    对于需要实现自定义请求的情况,这是需要做的全部工作:

    1. 继承Request类, 表示请求之后解析响应的类型。如果解析后的响应是String,那T就应该是String.
    2. 实现 parseNetworkResponse() 和deliverResponse()方法。

    parseNetworkResponse

    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));
        }
        // handle errors
    ...
    }
    

    NetworkResponse参数包含了返回的所有数据。包括byte[]形式的返回响应,HTTP状态码,响应头。

    注意: 实现必须返回一个Response,并包含指定类型T的相应对象、缓存数据或者一个错误信息,比如解析失败等。

    如果请求协议不是标准的缓存语法,可以自己构筑一个Cache.Entry

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

    Volley在工作线程调用parseNetworkResponse()方法。确保了昂贵的解析操作,例如将jpeg解码成位图,不会阻止UI线程。

    deliverResponse

    Volley从主线程中回调parseNetworkResponse()方法返回的对象。请求用回调接口回调回来这个对象:

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

    一个完整的自定义请求例子 GsonRequest

    	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));
            }
        }
    }
  • 相关阅读:
    poj 1262 地板覆盖问题
    混合图 (Standard IO)
    matrix
    麻将 (Standard IO)
    C#多线程编程之:异步事件调用
    使用线程池与专用线程
    C#多线程编程之:Timer(定时器)使用示例
    C#多线程编程之:异步方法调用
    WCF 快速入门
    c#实现每隔一段时间执行代码(多线程)
  • 原文地址:https://www.cnblogs.com/zharma/p/8335220.html
Copyright © 2011-2022 走看看