《Volley源码分析》Part1、Volley的使用以及介绍

来源:互联网 发布:张逗张花 知乎 编辑:程序博客网 时间:2024/05/21 19:43

1、简单介绍

因为说了是简单介绍,这里就不做复杂说明。

Volley是google方法出品的网络框架,在Goolge IO 2013大会上发布,目前在Android develop Training可以看到介绍,Training介绍原文地址,这个地址是需要翻墙的。

在附上介绍视频地址,同样也是需要翻墙的。Volley: Easy, Fast Networking for Android。

虽然现在大家项目中可能都使用的是 Retrofit,但是学习一下Volley还是很有必要的,总的来说是Android的google爸爸出品的,而且就目前状态来看,代码仍然在维护中。

image

2、Transmitting Network Data Using Volley(使用Volley进行网络数据传输)

Volley是一个HTTP库,它使Android应用程序的网络更容易,最重要的是更快。

Volley可以在Github上找到代码

Volley具有以下特点:

  • 1、自动调度网络请求
  • 2、支持多个并发网络连接
  • 3、具有标准的HTTP缓存一致性的硬盘和内存缓存
  • 4、支持请求优先级
  • 5、支持请求取消API,可以取消单个请求,也支持设置要取消的请求的块或范围
  • 6、更加容易自行定制
  • 7、强健的顺序性可以保证异步获取的数据正确的填充你的UI
  • 8、Debugging and tracing tools.

Volley擅长用于填充UI的RPC-TYPE操作,如将搜索结果页作为结构化数据抓取。它易于与任何协议集成,并为输出原始字符串、images和JSON提供了内建的支持。可以让你更加专注于具体的业务逻辑功能。

Volley不适合大型下载或者流媒体操作,因为为了方便解析Volley在内存中持有了所有的Response信息。对于大型下载可以考虑使用其他方式,比如DownloadManager。

如果想在你的项目中集成Volley,可以加入如下的代码

dependencies {    ...    compile 'com.android.volley:volley:1.0.0'}

当然你也可以clone下来代码,以module的形式加入到项目中

git clone https://github.com/google/volley

2、Sending a Simple Request(发送一个简单的请求)

2-1、添加INTERNET权限

 <uses-permission android:name="android.permission.INTERNET" />

2-2、使用RequestQueue

Volley提供了一个方便的方法Volley.newRequestQueue去配置RequestQueue,当然是用的默认值去开启队列。下面是使用例子。

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));    }}, new Response.ErrorListener() {    @Override    public void onErrorResponse(VolleyError error) {        mTextView.setText("That didn't work!");    }});// Add the request to the RequestQueue.queue.add(stringRequest);

Volley总是将解析的返回结果呈现在主线程中,这样方便了去做UI相关的填充处理操作,你可以方便的拿到数据去直接修改ui控件。它还提供了很多有用的功能,比如取消一个请求。

2-3、发送请求

发送一个请求,你只需要构造一个请求,并把它通过RequestQueue#add()方法添加到RequestQueue中去,在添加了请求之后,它将通过管道(pipeline)移动,得到服务,并对其原始相应进行解析和分发。

当你调用add()方法,Volley将运行一个缓存处理线程和一个网络调度线程池。当你添加一个请求到队列中,它将被缓存线程进行一个判断,如果请求对应的缓存服务可以工作,也就是说请求的缓存存在并且没有过期,缓存线程会进行解析并且分发返回结果到主线程(UI线程)。如果不能从缓存中获取(这里也包括缓存过期的情况),那么它就被放置在消息队列中。有一个网络任务线程去处理请求,执行HTTP事务,解析工作线程上的返回结果,将返回结果写入缓存,并将解析后的结果返回分发到主线程中。

note:诸如阻塞IO,解析数据等这种耗时操作都是在工作线程中完成的,你可以从任何线程添加一个请求,但是响应总是要在主线程。

image

2-4、取消请求

取消一个请求,在你的Request对象调用cancel()。一旦取消,Volley保证你的响应结果处理分发将永远不同被调用(个人理解是不管你网络请求处理结果如何,一旦取消,负责结果分发的处理就中断了)。这就意味着你可以在onStop中取消你的没有执行的请求,而且你完全不用去在你的请求结果处理返回部分检查getActivity() == null(反正这块处理逻辑都不会走了)。

要使用这种行为,你应该追踪你所有的飞行请求(in-flight: 应该理解为所有的已经请求,但是还没有拿到请求结果的请求),以便在合适的时机取消它们。这里有一个简单方法:你可以用一个标记(tag)和请求关联起来,然后你可以用这个标记来提供取消的范围。

Here is an example that uses a string value for the tag:

  • 1、Define your tag and add it to your requests.
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);
  • 2、In your activity’s onStop() method, cancel all requests that have this tag.
@Overrideprotected void onStop () {    super.onStop();    if (mRequestQueue != null) {        mRequestQueue.cancelAll(TAG);    }}

取消请求时要注意,如果你的请求结果需要推进一个状态或者另一个进程,你应该了解到,这个对应的处理(状态或者另一个进程相关操作)同样不会被调用到。

3、Setting Up a RequestQueue(配置RequestQueue)

3-1、设置网络和缓存

一个RequestQueue会做两个工作:传输网络请求和处理数据缓存。在Volley的自带工具箱(toolbox)中提供了默认实现:

DiskBaseCache提供一个与内存中索引一一对应的文件。BasicNetwork提供一个基于HTTP的网络传输协议客户端。

BasicNetwork是Volley的默认网络实现,一个网络必须与您的应用程序使用HTTP客户端初始化链接网络,通常是HttpUrlConnection

下面代码片段展示如何设置RequestQueue

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

如果你只想要做一次请求,不需要线程池,你可以创建一个RequestQueue在你任何需要的地方,在数据返回之后,调用stop()即可。更常用的方法是直接使用Volley.newRequestQueue()去执行一个简单的请求,并且保持RequestQueue在你的应用程序中单例存在。

3-2、使用单例模式

为了使整个应用的生命周期中,你的网络请求都可用,可以将RequestQueue设置为单例模式。

一个需要注意的点是,RequestQueue必须使用Application Context进行初始化,而不是Activity Context,这将确保了RequestQueue在整个应用的生命周期有效。

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 RequestQueueRequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).    getRequestQueue();// ...// Add a request (in this example, called stringRequest) to your RequestQueue.MySingleton.getInstance(this).addToRequestQueue(stringRequest);

4、Making a Standard Request(发起一个标准请求)

Volley内建支持了如下请求类型:

StringRequest: 指定一个url,请求返回字符串类型的结果。

JsonObjectRequestJsonArrayRequest,指定一个url,返回结果是JSON格式或者JSONAarray格式。

下面的例子,使用JsonObjectRequest展示:

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<JSONObject>() {    @Override    public void onResponse(JSONObject response) {        mTxtDisplay.setText("Response: " + response.toString());    }}, new Response.ErrorListener() {    @Override    public void onErrorResponse(VolleyError error) {        // TODO Auto-generated method stub    }});// Access the RequestQueue through your singleton class.MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

5、Implementing a Custom Request(自定义实现一个请求)

Volley的工具箱中提供了网络请求,缓存的默认实现,以及几种请求方式,如果这些都不满足你的实际使用的话,Volley也支持自定义实现。

5-1、自定义网络请求

想要自己实现网络请求,需要如下几步:

  • 1、继承Request<T>,这里的泛型T就是处理之后的返回结果类型,如果你期待的解析之后类型是String,可能你需要写成Request,具体的实现可以在Volley Toolbox中找到。
  • 2、实现抽象方法parseNetworkResponse()deliverResponse

parseNetworkResponse
这个方法主要是对于返回的结果集进行解析处理。

@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状态码,header信息等内容。

deliverResponse

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

5-2、Example: 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));        }    }}

6、实例代码

https://github.com/fireking0415/VolleySample

原创粉丝点击