1、简单介绍
因为说了是简单介绍,这里就不做复杂说明。
Volley是google方法出品的网络框架,在Goolge IO 2013大会上发布,目前在Android develop Training可以看到介绍,Training介绍原文地址,这个地址是需要翻墙的。
在附上介绍视频地址,同样也是需要翻墙的。Volley: Easy, Fast Networking for Android。
虽然现在大家项目中可能都使用的是 Retrofit,但是学习一下Volley还是很有必要的,总的来说是Android的google爸爸出品的,而且就目前状态来看,代码仍然在维护中。
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,解析数据等这种耗时操作都是在工作线程中完成的,你可以从任何线程添加一个请求,但是响应总是要在主线程。
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.
@Override
protected 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 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);
// ...
如果你只想要做一次请求,不需要线程池,你可以创建一个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 RequestQueue
RequestQueue 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,请求返回字符串类型的结果。
JsonObjectRequest和JsonArrayRequest,指定一个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
这个方法主要是对于返回的结果集进行解析处理。
@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
...
}
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));
}
}
}