转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50888340
本文出自:【顾林海的博客】
Volley这个网络框架大家并不陌生,它的优点网上一大堆, 适合网络通信频繁操作,并能同时实现多个网络通信、扩展性强、通过接口配置之类的优点。在写这篇文章之前,特意去了解并使用Volley这个网络框架,文章是对Volley的一点点理解,如有写得不到位的地方,欢迎大家指出。
使用Volley的通用步骤就是通过Volley暴露的newRequestQueue方法,创建的我们的RequestQueue,接着往我们的RequestQueue中添加Request( StringRequest、JsonRequest、ImageRequest,以及你自己定义的Request)。
private void initNetWork() {
RequestQueue queue = Volley.newRequestQueue(this);
String url = "";
StringRequest stringRequest = new StringRequest(Request.Method.GET,
url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
queue.add(stringRequest);
queue.start();
}
步骤很简单,总结就三步:
上面的实例通过get方式请求数据的,post的方式也是很简单,在创建Request的时候,第一个参数改为Request.Methode.POST:
private void initNetWork_1() {
RequestQueue queue = Volley.newRequestQueue(this);
String url = "";
StringRequest stringRequest = new StringRequest(Request.Method.POST,
url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
// TODO Auto-generated method stub
return super.getParams();
}
};
queue.add(stringRequest);
queue.start();
}
仔细查看上面POST和GET的不同方式的请求代码,可以看出POST的时候多了一个getParams方法,返回的Map类型,getParams获取的数据是用于向服务器提交的参数。
在这里我们看到,如果每次都去定义Map,然后往里面put键值对,这是一件很沮丧的事情,这里给出一个工具类,可以通过解析对象转换成我们需要的Map,代码如下:
package com.example.volleyproject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class MapUtil {
public static <T> Map<String, String> changeTtoMap(T m) {
HashMap<String, String> map = new HashMap<String, String>();
// 获取实体类的所有属性
Field[] field = m.getClass().getDeclaredFields();
// 遍历所有属性
for (int j = 0; j < field.length; j++) {
// 获取属性的名字
String name = field[j].getName();
String value = (String) getFieldValueObj(m, name);
if (value != null && !value.equals("")) {
map.put(name, value);
}
}
return map;
}
/** * 获取对应的属性值 * * @param target * 对象 * @param fname * Filed * @return */
public static Object getFieldValueObj(Object target, String fname) { // 获取字段值
// 如:username 字段,getUsername()
if (target == null || fname == null || "".equals(fname)) {// 如果类型不匹配,直接退出
return "";
}
Class clazz = target.getClass();
try { // 先通过getXxx()方法设置类属性值
String methodname = "get" + Character.toUpperCase(fname.charAt(0))
+ fname.substring(1);
Method method = clazz.getDeclaredMethod(methodname); // 获取定义的方法
if (!Modifier.isPublic(method.getModifiers())) { // 设置非共有方法权限
method.setAccessible(true);
}
return (Object) method.invoke(target); // 执行方法回调
} catch (Exception me) {// 如果get方法不存在,则直接设置类属性值
try {
Field field = clazz.getDeclaredField(fname); // 获取定义的类属性
if (!Modifier.isPublic(field.getModifiers())) { // 设置非共有类属性权限
field.setAccessible(true);
}
return (Object) field.get(target); // 获取类属性值
} catch (Exception fe) {
}
}
return "";
}
}
最后在getParams 调用这个工具类中的changeTtoMap方法即可:
private void initNetWork_1() {
RequestQueue queue = Volley.newRequestQueue(this);
String url = "";
final RequestParams params = new RequestParams();
params.id = "1";
params.name = "bill";
StringRequest stringRequest = new StringRequest(Request.Method.POST,
url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return MapUtil.changeTtoMap(params);
}
};
queue.add(stringRequest);
queue.start();
}
所有操作都是基于上面讲的三个步骤,第一步创建一个RequestQueue对象。
RequestQueue queue = Volley.newRequestQueue(this);
请求队列(RequestQueue)不需要出现多个,因此我们可以在Application中进行初始化。
/** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * * @param context A {@link Context} to use for creating the cache dir. * @return A started {@link RequestQueue} instance. */
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
方法很简单,创建的我们的请求队列,这里通过传入Context来确定我们缓存目录。
/** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * * @param context A {@link Context} to use for creating the cache dir. * @param stack An {@link HttpStack} to use for the network, or null for default. * @return A started {@link RequestQueue} instance. */
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
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 DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
在Volley中,http的处理请求,默认 Android2.3 及以上基于 HttpURLConnection的 HurlStack,2.3 以下基于 HttpClient 的 HttpClientStack,通过上面的这段代码可知:
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));
}
}
这里面的HurlStack和HttpClientStack都实现了HttpStack这个接口,HttpStack是用于http请求,返回请求结果的。
public interface HttpStack {
/** * Performs an HTTP request with the given parameters. * * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, * and the Content-Type header is set to request.getPostBodyContentType().</p> * * @param request the request to perform * @param additionalHeaders additional headers to be sent together with * {@link Request#getHeaders()} * @return the HTTP response */
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
public class HttpClientStack implements HttpStack {
}
public class HurlStack implements HttpStack{
}
在根据SDK版本确定使用哪个Http请求方式后创建BasicNetwork对象,BasicNetwork实现了Network接口:
/** * An interface for performing requests. */
public interface Network {
/** * Performs the specified request. * @param request Request to process * @return A {@link NetworkResponse} with data and caching metadata; will never be null * @throws VolleyError on errors */
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
NetWork用于 调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。 这里面的BasicNetwork就是一个具体是实现。
接着执行:
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
DiskBasedCache是我们缓存的目录:
public DiskBasedCache(File rootDirectory) {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
这里面定义了缓存的地址,默认最大是5MB。继续查看RequestQueue的构造器:
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在创建RequestQueue实例时进行了一系列的初始化(缓存、HttpStack的处理请求、线程池大小、返回结果的分发)。
NetworkDispatcher是一个线程, 用于调度处理网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
public class NetworkDispatcher extends Thread {
}
这里面的ResponseDelivery用于返回结果分发接口,目前只有基于ExecutorDelivery的 handler 对应线程内进行分发。
ExecutorDelivery通过内部的handler对应的线程进行返回结果的分发:
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
Executor框架是java 5引入的并发库,把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。
这里面传递进来的是new Handler(Looper.getMainLooper()),传入UI线程的Handler的目的是用于UI更新,比如通过通过Volley进行图片下载时的ImageView更新。
上面也提到过ResponseDelivery用于返回结果分发,目前只基于ExecutorDelivery的handler对应线程进行分发,由此我们找到数据分发的方法:
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
在postResponse、postError都调用了Executor.execute(Runnalbe)方法进行并发执行,ResponseDeliveryRunnable实现了Runnable接口并重写run方法:
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
run方法中的代码比较简单,进行数据的分发,这里面的mResponse就是我们返回的结果,通过mRequest.deliverResponse(mResponse.result);进行数据分发,这里面的Request是一个请求的抽象类,进入StringRequest类中,我们发现StringRequest实现了这个抽象类:
public class StringRequest extends Request<String>{
}
在StringRequest 中我们找到这么一段代码:
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
对比之前我们定义的StringRequest的代码:
StringRequest stringRequest = new StringRequest(Request.Method.GET,
url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
是不是很熟悉,最后结果就是通过ExecutorDelivery中handler对应线程进行分发处理的。
到这里请求队列(RequestQueue)就初始化完毕了。
接着就是定义我们的Request,Volley定义了现成的Request,像 StringRequest、JsonRequest、ImageReques,当然我们可以根据StringRequest一样定义我们自己的Request,只需实现Request这个抽象类:
package com.android.volley.toolbox;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import java.io.UnsupportedEncodingException;
/** * A canned request for retrieving the response body at a given URL as a String. */
public class StringRequest extends Request<String> {
private final Listener<String> mListener;
/** * Creates a new request with the given method. * * @param method the request {@link Method} to use * @param url URL to fetch the string at * @param listener Listener to receive the String response * @param errorListener Error listener, or null to ignore errors */
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/** * Creates a new GET request. * * @param url URL to fetch the string at * @param listener Listener to receive the String response * @param errorListener Error listener, or null to ignore errors */
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
查看StringRequest源码,重写了以下两个方法:
abstract protected void deliverResponse(T response);
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
NetworkResponse存放的是从服务器返回并以字节形式的数据。按照StringRequest我们可以定义GsonRequest:
public class GsonRequest<T> extends Request<T> {
private final Listener<T> mListener;
private Gson mGson;
private Class<T> mClass;
public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mGson = new Gson();
mClass = clazz;
mListener = listener;
}
public GsonRequest(String url, Class<T> clazz, Listener<T> listener,
ErrorListener errorListener) {
this(Method.GET, url, clazz, listener, errorListener);
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(mGson.fromJson(jsonString, mClass),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
到这里为止我们的队列以及请求响应都已定义好,最后通过请求队列的start方法进行请求:
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
CacheDispatcher和NetworkDispatcher中的quit方法:
public void quit() {
mQuit = true;
interrupt();
}
通过start方法对队列进行调度,方法中的先通过stop进行缓存和网络请求的调度终止,通过它们的quit方法中的interrupt方法进行线程中断,CacheDispatcher是 一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
由此start方法中的处理逻辑就一目了然了,先是经过缓存的请求,在未缓存过或是缓存失效时,再进入网络的请求,并判断结果是否要进行缓存。