前言
Volley是Google推出的一款网络请求框架,虽然已经很久了,但还是想想它的源码。Volley的网络请求实现,在Android 2.3以上使用的是HttpURLConnection,而2.3以下则是使用HttpClient实现。
简单使用
注意:在Android 6.0时,谷歌去掉了HttpClient的类,而Volley依赖HttpClient,所以需要进行配置才行
在模块的build.gradle中,添加
android {
useLibrary 'org.apache.http.legacy'
}
在AndroidManifest.xml中,也需要配置
还有别忘了,在AndroidManifest.xml中,添加网络权限喔
在Android 9.0时,不允许明文请求,还需要在清单的application节点中,进行配置,允许明文请求
先来看下GET请求,Volley是怎么发出的吧
一般为3个步骤:
- 创建RequestQueue请求队列
- 创建请求,设置成功和失败的回调
- 把请求加入队列,发出请求
//1.创建请求队列
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
//2.创建请求,设置回调
StringRequest request = new StringRequest("https://www.wanandroid.com/article/list/1/json", new Response.Listener() {
@Override
public void onResponse(String response) {
//请求成功
Log.d(TAG, response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
//请求失败
Log.e(TAG, error.getMessage(), error);
}
});
//3.加入队列,发起请求
requestQueue.add(request);
开始分析
先从创建RequestQueue请求队列开始,它是调用Volley.newRequestQueue(context),来创建的,所以先来看看Volley类
Volley
newRequestQueue()有3个重载方法,最终都是调用3个参数的newRequestQueue(context, stack, maxDiskCacheBytes),里面主要是做的是:
- 确定缓存目录
- 确定UA
- 根据当前Android版本号,在Android 2.3及其以上,创建实现HurlStack,它是HttpURLConnection实现的,2.3以下则创建实现HttpClientStack,它是HttpClient实现的
- 创建网络操作实现,Network是一个操作接口,具体实现类是BasicNetwork,它需要传入HttpStack
- 创建RequestQueue,创建DiskBasedCache并传入,它是一个缓存操作类,它的是Cache接口的具体实现,同时把Network实现传入
- 最后,调用RequestQueue的start()方法,开启队列
public class Volley {
/**
* 默认磁盘缓存目录名
*/
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 创建请求队列,支持设置请求实现和最大磁盘缓存大小
*
* @param stack 请求实现,传null则自动根据版本选择
* @param maxDiskCacheBytes 最大磁盘缓存大小,传-1则使用默认的5Ms
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
//缓存目录
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
//设置UA
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;
//根据传入的磁盘缓存大小,创建请求队列
if (maxDiskCacheBytes <= -1) {
//没有设置,使用默认值创建
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
} else {
//设置了,则使用指定的值
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//开启队列
queue.start();
return queue;
}
/**
* 创建请求队列,支持设置请求实现和最大磁盘缓存大小
*
* @param maxDiskCacheBytes 最大磁盘缓存大小
*/
public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {
return newRequestQueue(context, null, maxDiskCacheBytes);
}
/**
* 创建请求队列,支持设置请求实现
*
* @param stack 请求实现
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
return newRequestQueue(context, stack, -1);
}
/**
* 创建请求队列
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
RequestQueue
RequestQueue的构造中,除了保存传进来的Cache、Network,还有threadPoolSize以及ResponseDelivery
threadPoolSize就是线程池个数,代表处理请求的线程个数(但Volley并没有使用线程池,而是直接创建指定数量的线程),ResponseDelivery则为响应分发器,用来把请求结果或错误结果分发给我们设置的监听器
上面调用了RequestQueue的start()方法,这个方法中,主要是创建了一个CacheDispatcher和4个NetworkDispatcher,并且调用了他们的start()方法
start()方法中,传入了一个缓存队列,一个网络队列,Cache,ResponseDelivery,其中缓存队列和网络队列都是PriorityBlockingQueue类型,就是优先级阻塞队列,可以按优先级把请求排序,并且阻塞队列在被获取队列元素时,如果队列为空,就会阻塞在那里
/**
* 请求队列管理类
*/
public class RequestQueue {
/**
* 请求完成监听器
*/
public interface RequestFinishedListener {
/**
* 请求完成时,回调
*/
void onRequestFinished(Request request);
}
/**
* 用于生成请求序列号
*/
private final AtomicInteger mSequenceGenerator = new AtomicInteger();
/**
* Staging area for requests that already have a duplicate request in flight.
*
*
* - containsKey(cacheKey) indicates that there is a request in flight for the given cache
* key.
* - get(cacheKey) returns waiting requests for the given cache key. The in flight request
* is not contained in that list. Is null if no requests are staged.
*
*/
private final Map>> mWaitingRequests = new HashMap<>();
/**
* 正在请求中的请求集合
*/
private final Set> mCurrentRequests = new HashSet<>();
/**
* 请求的缓存队列,是一个PriorityBlockingQueue,可以根据优先级来出队
*/
private final PriorityBlockingQueue> mCacheQueue = new PriorityBlockingQueue<>();
/**
* 请求的网络队列,也是一个PriorityBlockingQueue
*/
private final PriorityBlockingQueue> mNetworkQueue = new PriorityBlockingQueue<>();
/**
* 请求分发器的数量,默认为4个
*/
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/**
* 缓存类,用于保存缓存和查询缓存
*/
private final Cache mCache;
/**
* 网络请求执行器
*/
private final Network mNetwork;
/**
* 网络请求结果分发器
*/
private final ResponseDelivery mDelivery;
/**
* 网络请求分发器数组
*/
private final NetworkDispatcher[] mDispatchers;
/**
* 缓存分发器
*/
private CacheDispatcher mCacheDispatcher;
/**
* 网络请求完成后的监听器集合
*/
private final List mFinishedListeners = new ArrayList<>();
/**
* 创建一个请求队列RequestQueue,要调用 {@link #start()} ,队列才会开始工作
*
* @param cache 用于把缓存到磁盘
* @param network 用于执行请求
* @param threadPoolSize 用于设置网络请求分发器的线程池数量
* @param delivery 用于回调请求结果的分发器
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* 创建一个请求队列RequestQueue,可以指定分发器的个数
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* 创建一个请求队列RequestQueue,使用默认配置
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
/**
* 让队列开始工作
*/
public void start() {
//停止正在进行的分发器,包括缓存分发器和网络分发器
stop();
//创建缓存分发器
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
//启动缓存分发器
mCacheDispatcher.start();
//根据定义的分发器数组数量,创建指定数量的网络分发器
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();
}
}
}
/**
* Gets a sequence number.
*/
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
/**
* Gets the {@link Cache} instance being used.
*/
public Cache getCache() {
return mCache;
}
/**
* 过滤器接口
*/
public interface RequestFilter {
//判断是否过滤
boolean apply(Request> request);
}
/**
* Cancels all requests in this queue for which the given filter applies.
*
* @param filter The filtering function to use
*/
public void cancelAll(RequestFilter filter) {
//同步锁
synchronized (mCurrentRequests) {
//遍历当前请求集合
for (Request> request : mCurrentRequests) {
//匹配到了,则取消请求
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* 取消指定tag标记的请求
*/
public void cancelAll(final Object tag) {
//Tag不可以为空
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request> request) {
//匹配到了
return request.getTag() == tag;
}
});
}
/**
* 添加一个请求到分发队列中
*/
public Request add(Request request) {
//请求保存该队列,用于标识该请求属于该队列
request.setRequestQueue(this);
//同步添加请求对正在进行的请求集合中
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
//给请求设置序列号
request.setSequence(getSequenceNumber());
//设置Marker标记为
request.addMarker("add-to-queue");
//如果请求不需要缓存,那添加到网络队列中就可以了
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
/**
* 结束请求
*/
void finish(Request request) {
//从正在进行中的请求集合中移除该请求
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
//通知回调,请求结束了
synchronized (mFinishedListeners) {
for (RequestFinishedListener listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
//如果该请求可以被缓存
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
/**
* 添加一个请求结束的回调
*/
public void addRequestFinishedListener(RequestFinishedListener listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.add(listener);
}
}
/**
* 移除设置的请求结束回调
*/
public void removeRequestFinishedListener(RequestFinishedListener listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.remove(listener);
}
}
}
CacheDispatcher
CacheDispatcher是一个缓存分发器,它继承于Thread,所以它是一个线程,那么肯定会复写run()方法
在run()方法中,主要做以下几件事
- 设置线程优先级为后台线程
- 缓存类初始化
- 死循环,不断从CacheQueue缓存队列中获取Request,try-catch,如果线程被打断,则判断退出标记,是的话,则退出,不是则进入到下一次循环。
- 判断是否被取消,被取消则进入下一次循环
- 先尝试从缓存类中,使用请求的缓存Key,获取缓存信息,如果获取不到,则把请求加入到NetworkQueue网络队列
- 如果缓存已过期,也加入到NetworkQueue网络队列
- 如果缓存命中,则从缓存中获取数据和请求头,再解析它们,得到响应Response,再通过ResponseDelivery,把响应结果发到主线程,回调给我们的监听器
public class CacheDispatcher extends Thread {
/**
* Debug模式标记
*/
private static final boolean DEBUG = VolleyLog.DEBUG;
/**
* 缓存队列,是一个BlockingQueue
*/
private final BlockingQueue> mCacheQueue;
/**
* 网络队列,也是一个BlockingQueue
*/
private final BlockingQueue> mNetworkQueue;
/**
* 缓存类
*/
private final Cache mCache;
/**
* 网络请求结果分发器
*/
private final ResponseDelivery mDelivery;
/**
* 是否退出的标记
*/
private volatile boolean mQuit = false;
/**
* 构造方法,创建一个缓存分发器,必须调用 {@link #start()} 才能开始处理
*
* @param cacheQueue 缓存队列
* @param networkQueue 网络队列
* @param cache 缓存类
* @param delivery 请求结果分发器
*/
public CacheDispatcher(
BlockingQueue> cacheQueue, BlockingQueue> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* 停止缓存分发器
*/
public void quit() {
//设置标记
mQuit = true;
//中断线程
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//设置线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//缓存初始化
mCache.initialize();
Request> request;
//死循环,不断从队列中取出请求
while (true) {
//每次执行前,清除之前的请求对象,避免泄露
request = null;
try {
//从缓存队列中获取一个请求
request = mCacheQueue.take();
} catch (InterruptedException e) {
//被打断了,则退出
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("cache-queue-take");
//请求被取消,结束请求即可
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
//尝试从缓存中查找缓存信息
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
//没有找到缓存,加入到网络请求队列
mNetworkQueue.put(request);
continue;
}
//缓存过期,也加入网络请求队列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//命中缓存,解析它
request.addMarker("cache-hit");
Response> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
//完全没有过期,在主线程回调结果
mDelivery.postResponse(request, response);
} else {
//软过期,在主线程回调结果,但也要请求网络
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
//设置为软过期标记
response.intermediate = true;
//在主线程中回调
final Request> finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//把请求,添加到请求队列中
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
//添加失败,不处理了
}
}
});
}
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
}
}
}
}
NetworkDispatcher
NetworkDispatcher和CacheDispatcher类似,也是继承于Thread,核心逻辑也是run()方法
在run()方法中,主要做以下几件事
- 设置线程优先级为后台线程
- 死循环,不断从网络队列中,获取Request请求,try-catch,如果线程被中断了,判断退出标志位,是则退出,否则进行下一次循环
- 判断请求是否被取消,被取消了则结束请求
- 通过Network,发起网络请求,获取NetworkResponse响应对象
- 解析NetworkResponse响应对象为Response对象
- 判断该请求是否需要被缓存,如果需要,则通过Cache操作类把请求的响应写入磁盘缓存
- 通过ResponseDelivery,把响应结果发送到主线程,进行成功监听器回调
- 最外层有try-catch,如果发生异常Exception,则通过ResponseDelivery回调错误监听器
Request
Request是一个抽象类,它上面有一个泛型,泛型为具体响应解析时的类型,这个基类提供了公共逻辑,不同的响应类型由子类实现,例如响应为字符串,则是StringRequest,Json请求分3个类,基类JsonRequest,JsonObject类型是JsonObjectRequest,JsonArray类型是JsonArrayRequest
- Request
Request类实现了Comparable接口,实现compareTo()方法,目的是用于队列按优先级排序,如果优先级相等,则按照入队的顺序进行排序(先进先出)
public abstract class Request implements Comparable> {
/**
* 默认的参数编码
*/
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
/**
* 支持的请求方法
*/
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
int PATCH = 7;
}
/**
* An event log tracing the lifetime of this request; for debugging.
*/
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
/**
* 当前请求的请求方法,支持 GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, and PATCH.
*/
private final int mMethod;
/**
* 请求的请求地址Url
*/
private final String mUrl;
/**
* 出现 3xx http响应码的重定向url
*/
private String mRedirectUrl;
/**
* 请求的唯一标识
*/
private final String mIdentifier;
/**
* Default tag for {@link TrafficStats}.
*/
private final int mDefaultTrafficStatsTag;
/**
* 错误的回调监听器
*/
private Response.ErrorListener mErrorListener;
/**
* 请求的序列号,用于请求排序,先进先出
*/
private Integer mSequence;
/**
* 请求绑定请求队列
*/
private RequestQueue mRequestQueue;
/**
* 是否需要缓存,默认开启
*/
private boolean mShouldCache = true;
/**
* 请求是否被取消的标志位
*/
private boolean mCanceled = false;
/**
* 请求是否已被响应
*/
private boolean mResponseDelivered = false;
/**
* 重试策略
*/
private RetryPolicy mRetryPolicy;
/**
* 缓存信息对象
*/
private Cache.Entry mCacheEntry = null;
/**
* 请求的Tag,取消请求时使用
*/
private Object mTag;
/**
* Creates a new request with the given URL and error listener. Note that
* the normal response listener is not provided here as delivery of responses
* is provided by subclasses, who have a better idea of how to deliver an
* already-parsed response.
*
* @deprecated Use {@link #Request(int, String, Response.ErrorListener)}.
*/
@Deprecated
public Request(String url, Response.ErrorListener listener) {
this(Method.DEPRECATED_GET_OR_POST, url, listener);
}
/**
* 构造方法,创建一个Request对象
*
* @param method 请求方法
* @param url 请求Url
* @param listener 错误回调监听器
*/
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
//创建请求的唯一标识
mIdentifier = createIdentifier(method, url);
mErrorListener = listener;
//设置默认的重试策略
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
/**
* Return the method for this request. Can be one of the values in {@link Method}.
*/
public int getMethod() {
return mMethod;
}
/**
* Set a tag on this request. Can be used to cancel all requests with this
* tag by {@link RequestQueue#cancelAll(Object)}.
*
* @return This Request object to allow for chaining.
*/
public Request> setTag(Object tag) {
mTag = tag;
return this;
}
/**
* Returns this request's tag.
*
* @see Request#setTag(Object)
*/
public Object getTag() {
return mTag;
}
/**
* @return this request's {@link Response.ErrorListener}.
*/
public Response.ErrorListener getErrorListener() {
return mErrorListener;
}
/**
* @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
*/
public int getTrafficStatsTag() {
return mDefaultTrafficStatsTag;
}
/**
* 返回Url的host(主机地址)的hashcode,没有的话,返回0
*/
private static int findDefaultTrafficStatsTag(String url) {
if (!TextUtils.isEmpty(url)) {
Uri uri = Uri.parse(url);
if (uri != null) {
String host = uri.getHost();
if (host != null) {
return host.hashCode();
}
}
}
return 0;
}
/**
* Sets the retry policy for this request.
*
* @return This Request object to allow for chaining.
*/
public Request> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}
/**
* Adds an event to this request's event log; for debugging.
*/
public void addMarker(String tag) {
if (MarkerLog.ENABLED) {
mEventLog.add(tag, Thread.currentThread().getId());
}
}
/**
* 通知RequestQueue队列,该请求已经结束
*/
void finish(final String tag) {
if (mRequestQueue != null) {
//通知队列,移除掉该请求
mRequestQueue.finish(this);
onFinish();
}
//打印日志
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
//判断当前线程如果是不是主线程
if (Looper.myLooper() != Looper.getMainLooper()) {
//不是主线程,通过Handler,保证Log打印是有序的
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
/**
* 结束时,清除监听器
*/
protected void onFinish() {
mErrorListener = null;
}
/**
* Associates this request with the given queue. The request queue will be notified when this
* request has finished.
*
* @return This Request object to allow for chaining.
*/
public Request> setRequestQueue(RequestQueue requestQueue) {
mRequestQueue = requestQueue;
return this;
}
/**
* Sets the sequence number of this request. Used by {@link RequestQueue}.
*
* @return This Request object to allow for chaining.
*/
public final Request> setSequence(int sequence) {
mSequence = sequence;
return this;
}
/**
* Returns the sequence number of this request.
*/
public final int getSequence() {
if (mSequence == null) {
throw new IllegalStateException("getSequence called before setSequence");
}
return mSequence;
}
/**
* Returns the URL of this request.
*/
public String getUrl() {
return (mRedirectUrl != null) ? mRedirectUrl : mUrl;
}
/**
* Returns the URL of the request before any redirects have occurred.
*/
public String getOriginUrl() {
return mUrl;
}
/**
* Returns the identifier of the request.
*/
public String getIdentifier() {
return mIdentifier;
}
/**
* Sets the redirect url to handle 3xx http responses.
*/
public void setRedirectUrl(String redirectUrl) {
mRedirectUrl = redirectUrl;
}
/**
* 返回这个请求的缓存Key,默认由请求方法和请求Url组合
*/
public String getCacheKey() {
return mMethod + ":" + mUrl;
}
/**
* 设置缓存信息对象
*/
public Request> setCacheEntry(Cache.Entry entry) {
mCacheEntry = entry;
return this;
}
/**
* 返回缓存信息对象,如果没有则为null
*/
public Cache.Entry getCacheEntry() {
return mCacheEntry;
}
/**
* 标记该请求已被取消,如果被取消,则不会进行回调
*/
public void cancel() {
mCanceled = true;
}
/**
* 返回该请求是否被取消了
*/
public boolean isCanceled() {
return mCanceled;
}
/**
* 返回配置的请求头
*
* @throws AuthFailureError In the event of auth failure
*/
public Map getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* Returns a Map of POST parameters to be used for this request, or null if
* a simple GET should be used. Can throw {@link AuthFailureError} as
* authentication may be required to provide these values.
*
* Note that only one of getPostParams() and getPostBody() can return a non-null
* value.
*
* @throws AuthFailureError In the event of auth failure
* @deprecated Use {@link #getParams()} instead.
*/
@Deprecated
protected Map getPostParams() throws AuthFailureError {
return getParams();
}
/**
* Returns which encoding should be used when converting POST parameters returned by
* {@link #getPostParams()} into a raw POST body.
*
* This controls both encodings:
*
* - The string encoding used when converting parameter names and values into bytes prior
* to URL encoding them.
* - The string encoding used when converting the URL encoded parameters into a raw
* byte array.
*
*
* @deprecated Use {@link #getParamsEncoding()} instead.
*/
@Deprecated
protected String getPostParamsEncoding() {
return getParamsEncoding();
}
/**
* @deprecated Use {@link #getBodyContentType()} instead.
*/
@Deprecated
public String getPostBodyContentType() {
return getBodyContentType();
}
/**
* 返回Body
*
* @throws AuthFailureError In the event of auth failure
* @deprecated Use {@link #getBody()} instead.
*/
@Deprecated
public byte[] getPostBody() throws AuthFailureError {
// Note: For compatibility with legacy clients of volley, this implementation must remain
// here instead of simply calling the getBody() function because this function must
// call getPostParams() and getPostParamsEncoding() since legacy clients would have
// overridden these two member functions for POST requests.
Map postParams = getPostParams();
if (postParams != null && postParams.size() > 0) {
return encodeParameters(postParams, getPostParamsEncoding());
}
return null;
}
/**
* 返回POST和PUT时的请求参数,子类可复写该方法设置自定义参数
*/
protected Map getParams() throws AuthFailureError {
return null;
}
/**
* 返回在POST和PUT时,参数转换使用的编码
*
* This controls both encodings:
*
* - The string encoding used when converting parameter names and values into bytes prior
* to URL encoding them.
* - The string encoding used when converting the URL encoded parameters into a raw
* byte array.
*
*/
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}
/**
* 返回请求的Content-Type
*/
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
/**
* 返回请求体,请求方式为:POST和PUT时
*
* By default, the body consists of the request parameters in
* application/x-www-form-urlencoded format. When overriding this method, consider overriding
* {@link #getBodyContentType()} as well to match the new body format.
*
* @throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* 将请求参数Map,转换为指定编码的字符串,例如:https://www.baidu.com/?a=xxx&b=yyy
*
* Converts params
into an application/x-www-form-urlencoded encoded string.
*
* @param params 参数Map
* @param paramsEncoding 编码
*/
private byte[] encodeParameters(Map params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
/**
* 设置该请求是否可以被缓存
*
* @param shouldCache 是否可以被缓存
*/
public final Request> setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
return this;
}
/**
* 该请求是否应该被缓存
*/
public final boolean shouldCache() {
return mShouldCache;
}
/**
* 优先级,请求顺序会按照优先级进行处理,高优先级会优先于低优先级执行,默认按照先进先出的规则
*/
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
/**
* 返回请求的优先级 {@link Priority},默认优先级为 {@link Priority#NORMAL}
*/
public Priority getPriority() {
return Priority.NORMAL;
}
/**
* 获取请求的超时时间
*/
public final int getTimeoutMs() {
return mRetryPolicy.getCurrentTimeout();
}
/**
* 返回该请求的重试策略
*/
public RetryPolicy getRetryPolicy() {
return mRetryPolicy;
}
/**
* 标记该请求已被响应过了
*/
public void markDelivered() {
mResponseDelivered = true;
}
/**
* 返回该请求是否被响应过了
*/
public boolean hasHadResponseDelivered() {
return mResponseDelivered;
}
/**
* 抽象方法,子类进行实现,这个方法在子线程中调用,
*
* @param response 网络响应
* @return 解析后的响应,如果出错,则返回null
*/
abstract protected Response parseNetworkResponse(NetworkResponse response);
/**
* 解析请求错误,子类可以复写该方法,返回更加具体的错误类型,默认类型是VolleyError
*/
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
/**
* 抽象方法,子类必须实现,该方法用于通知监听器获取响应结果
*
* @param response 响应
*/
abstract protected void deliverResponse(T response);
/**
* 请求失败时,回调错误监听器
*
* @param error 错误信息
*/
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
/**
* 比较方法
*
* 优先按照优先级进行排序,其次再按照序列号进行先进先出排序
*/
@Override
public int compareTo(Request other) {
//获取要比较的2个请求的请求优先级
Priority left = this.getPriority();
Priority right = other.getPriority();
//默认优先级为NORMAL
//如果优先级相等,则比较请求加入的顺序排序,先进先出
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
@Override
public String toString() {
String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
+ getPriority() + " " + mSequence;
}
/**
* 唯一标识的生成次数
*/
private static long sCounter;
/**
* 创建请求的唯一标识,它是通过请求方法和请求Url组合成一个字符串后,进行sha1Hash算法计算得出
*
* sha1(Request:method:url:timestamp:counter)
*
* @param method 请求方法
* @param url 请求Url
* @return sha1 hash string
*/
private static String createIdentifier(final int method, final String url) {
return InternalUtils.sha1Hash("Request:" + method + ":" + url +
":" + System.currentTimeMillis() + ":" + (sCounter++));
}
}
- StringRequest
public class StringRequest extends Request {
private Listener mListener;
/**
* 创建一个StringRequest,可以指定请求方法
*
* @param method 请求方法 {@link Method}
* @param url URL
* @param listener 响应监听器
* @param errorListener 错误监听器
*/
public StringRequest(int method, String url, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/**
* 创建一个StringRequest,使用GET请求
*
* @param url URL
* @param listener 响应监听器
* @param errorListener 错误监听器
*/
public StringRequest(String url, Listener listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void onFinish() {
super.onFinish();
//请求完成时,清理回调
mListener = null;
}
@Override
protected void deliverResponse(String response) {
//回调响应
if (mListener != null) {
mListener.onResponse(response);
}
}
@Override
protected Response 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));
}
}
- JsonRequest
JSON请求基类
public abstract class JsonRequest extends Request {
/**
* 默认字符集
*/
protected static final String PROTOCOL_CHARSET = "utf-8";
/**
* JSON请求的Content-Type
*/
private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
/**
* 响应监听器
*/
private Listener mListener;
/**
* JSON体
*/
private final String mRequestBody;
/**
* 废弃构造方法,默认GET请求,如果 {@link #getPostBody()} 或 {@link #getPostParams()} 被复写,则为POST请求
*/
public JsonRequest(String url, String requestBody, Listener listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
/**
* 手动指定请求方法,进行JSON请求
*/
public JsonRequest(int method, String url, String requestBody, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
}
@Override
protected void onFinish() {
super.onFinish();
//请求结束,清理回调
mListener = null;
}
@Override
protected void deliverResponse(T response) {
//回调响应成功
if (mListener != null) {
mListener.onResponse(response);
}
}
/**
* 子类进行JsonObject或JsonArray的处理
*
* @param response 响应实体
*/
@Override
abstract protected Response parseNetworkResponse(NetworkResponse response);
/**
* @deprecated Use {@link #getBodyContentType()}.
*/
@Override
public String getPostBodyContentType() {
return getBodyContentType();
}
/**
* @deprecated Use {@link #getBody()}.
*/
@Override
public byte[] getPostBody() {
return getBody();
}
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
@Override
public byte[] getBody() {
//JSON字符串转换为byte数组
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
}
- JsonObjectRequest
JsonObject类型的请求,主要在parseNetworkResponse()方法,把字符串Json解析为JSONObject类型
public class JsonObjectRequest extends JsonRequest {
//省略构造方法
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
//解析响应为字符串
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
//再把字符串解析为JSONObject返回
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException | JSONException e) {
return Response.error(new ParseError(e));
}
}
}
- JsonArrayRequest
public class JsonArrayRequest extends JsonRequest {
//省略构造方法
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
//解析响应为字符串
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
//再把字符串解析为JSONArray返回
return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException | JSONException e) {
return Response.error(new ParseError(e));
}
}
}
Network
Network接口,是一个网络请求接口,只有一个performRequest()方法,目的就是执行请求,他只有一个实现类BasicNetwork,内部通过HttpStack的进行真正的接口请求,那为什么不直接使用HttpStack还要套一层呢?
因为,Network还负责响应的转换,异常状态码的处理,请求的重定向,以及请求失败后的重试逻辑(委托给RetryPolicy,该类是重试策略类,只有一个唯一子类DefaultRetryPolicy,使用方也可以拓展子类进行不同策略)
performRequest()方法中,重试的实现,使用死循环,如果出错会进行重试,如果重试次数到了,则会抛出异常,中断这个死循环
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
/**
* 判断是否是慢请求的超时时间
*/
private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static final int DEFAULT_POOL_SIZE = 4096;
/**
* HttpStack实现类
*/
protected final HttpStack mHttpStack;
/**
* 缓冲池
*/
protected final ByteArrayPool mPool;
/**
* 构造方法,可以指定HttpStack实现,使用默认的缓冲池
*/
public BasicNetwork(HttpStack httpStack) {
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* 构造方法,可以指定HttpStack实现和缓冲池
*
* @param httpStack HttpStack实现
* @param pool 缓冲池
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
//开始时间
long requestStart = SystemClock.elapsedRealtime();
//死循环,如果成功则跳出循环,出错会重试,重试到一定次数后,抛出异常,跳出循环
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
//创建请求头
Map headers = new HashMap();
//添加缓存请求头
addCacheHeaders(headers, request.getCacheEntry());
//发起请求
httpResponse = mHttpStack.performRequest(request, headers);
//获取响应状态行
StatusLine statusLine = httpResponse.getStatusLine();
//获取响应状态码
int statusCode = statusLine.getStatusCode();
//转换响应头
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
//处理缓存和验证
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
//301和302状态码,是重定向,301代表请求的资源永久被移除了,而302是资源还在,但临时地跳转到另外一个链接
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
}
//处理内容
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
//处理没有内容的响应
responseContents = new byte[0];
}
//打印慢请求
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
//200和299的状态码处理
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
//返回响应
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
//301、302重定向
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
} else {
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
}
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
//401和403,进行重试
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
//301、302,重试到新地铁
attemptRetryOnException("redirect",
request, new RedirectError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
//其他则是服务器异常
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
/**
* 慢请求打印
*/
private void logSlowRequests(long requestLifetime, Request> request,
byte[] responseContents, StatusLine statusLine) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
"[rc=%d], [retryCount=%s]", request, requestLifetime,
responseContents != null ? responseContents.length : "null",
statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
}
}
/**
* 重试请求,如果达到重试次数会抛出异常
*
* @param logPrefix Log打印的前缀
* @param request 请求对象
* @param exception 到达重试次数后,会抛出的异常
*/
private static void attemptRetryOnException(String logPrefix, Request> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
/**
* 添加Http缓存头
*/
private void addCacheHeaders(Map headers, Entry entry) {
//该请求不需要缓存,不需要添加
if (entry == null) {
return;
}
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
/**
* 打印错误
*/
protected void logError(String what, String url, long start) {
long now = SystemClock.elapsedRealtime();
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
}
/**
* 把HttpEntity的内容转换到byte数组中
*/
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
byte[] buffer = null;
try {
InputStream in = entity.getContent();
if (in == null) {
throw new ServerError();
}
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
entity.consumeContent();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
mPool.returnBuf(buffer);
bytes.close();
}
}
/**
* 把 Headers[] 转换到 Map
*/
protected static Map convertHeaders(Header[] headers) {
Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
}
RetryPolicy
RetryPolicy类是重试策略接口,有一个默认实例DefaultRetryPolicy,里面定义了重试次数和超时时间,以及一个重试间隔时间的间隔因子(每次重试,重试的间隔时间会成倍数增加)
主要是retry()方法,该方法会统计累计的重试次数,如果到达了最大重试次数,则抛出retry()方法传入的异常
- RetryPolicy
public interface RetryPolicy {
/**
* 返回当前超时时间(用于日志记录)
*/
int getCurrentTimeout();
/**
* 返回当前重试次数(用于日志记录)
*/
int getCurrentRetryCount();
/**
* 重试
*/
void retry(VolleyError error) throws VolleyError;
}
- DefaultRetryPolicy
public class DefaultRetryPolicy implements RetryPolicy {
/**
* 超时时间
*/
private int mCurrentTimeoutMs;
/**
* 已重试的次数
*/
private int mCurrentRetryCount;
/**
* 最大重试次数
*/
private final int mMaxNumRetries;
/**
* 失败后,重连的间隔因子
*/
private final float mBackoffMultiplier;
/**
* 默认超时时间
*/
public static final int DEFAULT_TIMEOUT_MS = 2500;
/**
* 默认重试次数
*/
public static final int DEFAULT_MAX_RETRIES = 0;
/**
* 默认失败之后重连的间隔因子为1
*/
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* 使用默认参数,进行构造
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* 构造方法
*
* @param initialTimeoutMs 超时时间
* @param maxNumRetries 最大重试次数
* @param backoffMultiplier 重试间隔
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* 获取超时时间
*/
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
/**
* 获取,已重试的次数
*/
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* 获取,失败后,重连的间隔因子
*/
public float getBackoffMultiplier() {
return mBackoffMultiplier;
}
/**
* 重试
*/
@Override
public void retry(VolleyError error) throws VolleyError {
//重试1次,记录数量
mCurrentRetryCount++;
//计算重试时间
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
//到达重试最大次数,还是失败了,抛异常
throw error;
}
}
/**
* 判断是否还可以重试,还可以重试返回true,不可以重试返回false
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
HttpStack
HttpStack是请求接口,有2个实现类,HurlStack,它是HttpURLConnection实现,HttpClientStack,它是HttpClient实现
- HttpStack
只有一个performRequest()方法,这个方法就是处理具体的Request请求,以及附带的请求头
public interface HttpStack {
/**
* 使用指定参数,执行Http请求
*
* request.getPostBody() == null,则发送GET请求,不为null则发送POST请求
* 并且,Content-Type请求头的取值从 request.getPostBodyContentType() 中获取
*
* @param request 请求对象
* @param additionalHeaders 附带的请求头
* @return HTTP的响应
*/
HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError;
}
- HurlStack
内部使用HttpURLConnection发出请求,而响应实体HttpResponse,因为要统一返回值类型,统一都是HttpClient的类,所以会有转换的过程
public class HurlStack implements HttpStack {
/**
* Content-Type请求头标记
*/
private static final String HEADER_CONTENT_TYPE = "Content-Type";
/**
* URL转换接口
*/
public interface UrlRewriter {
/**
* 在请求前,可以重写URL
*
* @param originalUrl 原始URL
* @return 返回重写后的URL,返回null则代表这个请求不应该被请求,会抛出异常
*/
String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
private final SSLSocketFactory mSslSocketFactory;
public HurlStack() {
this(null);
}
/**
* @param urlRewriter 重写URL
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* @param urlRewriter 重写URL
* @param sslSocketFactory 用于HTTPS连接的SSL工厂
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
@Override
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
//获取请求Url
String url = request.getUrl();
//获取请求头
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//请求前,可以重写URL
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
//解析URL
URL parsedUrl = new URL(url);
//打开URL连接
HttpURLConnection connection = openConnection(parsedUrl, request);
//设置请求头
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//设置请求参数和请求方法
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
//获取响应码
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
//响应状态码无法获取会返回-1,要抛出异常给调用方
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
//设置响应码和响应信息到响应对象中
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
//有响应体则把响应体转换为HttpEntity设置到响应中
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
//设置头
for (Entry> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
//返回响应信息
return response;
}
/**
* 检查响应是否有响应体
*
* @param requestMethod 请求方法
* @param responseCode 响应状态码
*/
private static boolean hasResponseBody(int requestMethod, int responseCode) {
return requestMethod != Method.HEAD
&& !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
&& responseCode != HttpStatus.SC_NO_CONTENT
&& responseCode != HttpStatus.SC_NOT_MODIFIED;
}
/**
* 把指定的HttpURLConnection转换为HttpEntity
*/
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
/**
* 以指定的URL,创建一个HttpURLConnection连接
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
/**
* 打开一个带参数的HttpURLConnection
*
* @param url URL地址
* @return 链接
* @throws IOException 异常
*/
private HttpURLConnection openConnection(URL url, Request> request) throws IOException {
HttpURLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
//设置超时时间
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
//如果是https链接,设置调用时配置的SSL工厂
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection) connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
/**
* 设置请求参数和请求方法
*/
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request> request) throws IOException, AuthFailureError {
//设置请求方法
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
//一种不建议使用的方式,如果Body不为空,则设置为POST请求
byte[] postBody = request.getPostBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
connection.setDoOutput(true);
connection.setRequestMethod("POST");
//设置Content-Type
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
//设置请求体
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case Method.GET:
//默认不设置请求头的话,HttpURLConnection的请求方法为GET,这里再显式设置一下
connection.setRequestMethod("GET");
break;
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
/***
* 设置请求体,如果有的话
*/
private static void addBodyIfExists(HttpURLConnection connection, Request> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
}
- HttpClientStack
public class HttpClientStack implements HttpStack {
/**
* HttpClient实例
*/
protected final HttpClient mClient;
/**
* Content-Type标识
*/
private final static String HEADER_CONTENT_TYPE = "Content-Type";
public HttpClientStack(HttpClient client) {
mClient = client;
}
/**
* 给HttpUriRequest对象设置请求头
*/
private static void addHeaders(HttpUriRequest httpRequest, Map headers) {
for (String key : headers.keySet()) {
httpRequest.setHeader(key, headers.get(key));
}
}
/**
* Map请求参数Map转List
*/
@SuppressWarnings("unused")
private static List getPostParameterPairs(Map postParams) {
List result = new ArrayList(postParams.size());
for (String key : postParams.keySet()) {
result.add(new BasicNameValuePair(key, postParams.get(key)));
}
return result;
}
@Override
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
//创建请求
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
//添加请求头
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
//请求前回调一下,子类可以重写做额外的处理
onPrepareRequest(httpRequest);
//获取请求参数
HttpParams httpParams = httpRequest.getParams();
//获取超时时间
int timeoutMs = request.getTimeoutMs();
//设置超时时间
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
//执行请求
return mClient.execute(httpRequest);
}
/**
* 为请求创建对应的HttpUriRequest子类
*/
@SuppressWarnings("deprecation")
/* protected */ static HttpUriRequest createHttpRequest(Request> request,
Map additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
//有请求体,则设置为POST请求
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
//没有请求体,则设置给GET请求
return new HttpGet(request.getUrl());
}
}
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
//设置Content-Type
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
//设置请求体
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
case Method.HEAD:
return new HttpHead(request.getUrl());
case Method.OPTIONS:
return new HttpOptions(request.getUrl());
case Method.TRACE:
return new HttpTrace(request.getUrl());
case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request);
return patchRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
/**
* 设置请求体,如果不为空
*/
private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
Request> request) throws AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
HttpEntity entity = new ByteArrayEntity(body);
httpRequest.setEntity(entity);
}
}
/**
* 在执行请求前回调,子类可以复写该方法做额外处理
*/
protected void onPrepareRequest(HttpUriRequest request) throws IOException {
// Nothing.
}
/**
* PATCH类型请求,这个请求方式在安卓中不存在,所以在这里定义
*/
public static final class HttpPatch extends HttpEntityEnclosingRequestBase {
public final static String METHOD_NAME = "PATCH";
public HttpPatch() {
super();
}
public HttpPatch(final URI uri) {
super();
setURI(uri);
}
/**
* @throws IllegalArgumentException if the uri is invalid.
*/
public HttpPatch(final String uri) {
super();
setURI(URI.create(uri));
}
@Override
public String getMethod() {
return METHOD_NAME;
}
}
}
ResponseDelivery
ResponseDelivery是响应分发器接口,主要是把请求响应或请求错误信息,发送到主线程中,回调我们的响应监听器和错误监听器,至于线程切换自然是使用Handler
ResponseDelivery接口,有一个子类ExecutorDelivery
- ResponseDelivery
public interface ResponseDelivery {
/**
* 传递响应
*/
void postResponse(Request> request, Response> response);
/**
* 传递响应,并且附带的Runnable将在传递之后执行
*/
void postResponse(Request> request, Response> response, Runnable runnable);
/**
* 传递一个错误
*/
void postError(Request> request, VolleyError error);
}
- ExecutorDelivery
发现会把Handler封装成一个Executor,这个类是线程池中的类,只有一个execute()方法,执行传入的Runnable
public class ExecutorDelivery implements ResponseDelivery {
/**
* 传递响应的线程切换Executor,一般为主线程
*/
private final Executor mResponsePoster;
/**
* 构造方法,传入Handler
*
* @param handler 通过这个 {@link Handler} 进行回调
*/
public ExecutorDelivery(final Handler handler) {
//创建Executor,它会使用Handler进行执行
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* 构造方法,传入指定的Executor进行执行和回调
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@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));
}
/**
* 这个Runnable用于包装,用于在主线程回调
*/
@SuppressWarnings("rawtypes")
private static class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
//请求被取消了,不进行回调
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
//响应成功或失败,这里会回调调用方的监听器
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//如果是一个中间响应,标记一下
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
//不是中间响应,标记完成
mRequest.finish("done");
}
//执行额外的Runnable
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
分析到这里,整个请求链路基本清晰了,剩下Cache缓存接口还要分析下
Cache
Cache是缓存操作接口,有2个子类,DiskBasedCache是磁盘缓存类,NoCache则是不进行缓存的类,是对接口的所有方法进行空实现
- Cache
public interface Cache {
/**
* 查询缓存
*
* @param key 缓存Ke
* @return An {@link Entry} or null in the event of a cache miss
*/
Entry get(String key);
/**
* 添加或更新缓存
*
* @param key 缓存Key
* @param entry Data to store and metadata for cache coherency, TTL, etc.
*/
void put(String key, Entry entry);
/**
* 缓存初始化,该方法在子线程中回调
*/
void initialize();
/**
* 让一个缓存失效
*
* @param key 缓存Key
* @param fullExpire true为完全过期,false为软过期
*/
void invalidate(String key, boolean fullExpire);
/**
* 移除一个缓存
*
* @param key 缓存Key
*/
void remove(String key);
/**
* 清空缓存
*/
void clear();
/**
* 缓存实体,保存请求数据
*/
class Entry {
/**
* 缓存的数据
*/
public byte[] data;
/**
* ETag 保证缓存一致性
*/
public String etag;
/**
* 服务端返回数据的时间
*/
public long serverDate;
/**
* 缓存对象的最后修改日期
*/
public long lastModified;
/**
* 记录TTL
*/
public long ttl;
/**
* 记录 Soft TTL
*/
public long softTtl;
/**
* 服务端返回的不可变的响应头
*/
public Map responseHeaders = Collections.emptyMap();
/**
* 返回true,则代表过期
*/
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/**
* 是否需要刷新数据
*/
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
}
- DiskBasedCache
/**
* 缓存实现,缓存到磁盘上指定目录,默认缓存大小为5M,但可以配置
*/
public class DiskBasedCache implements Cache {
/**
* LinkedHashMap实现的LRU算法,按照使用的顺序进行排序
*/
private final Map mEntries = new LinkedHashMap<>(16, .75f, true);
/**
* 当前缓存的总大小,以字节为单位
*/
private long mTotalSize = 0;
/**
* 缓存的根目录
*/
private final File mRootDirectory;
/**
* 最大缓存容量
*/
private final int mMaxCacheSizeInBytes;
/**
* 默认的最大缓存容量,默认5M
*/
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
/**
* 缓存的负载因子,到达这个点后,会进行缓存清理
*/
private static final float HYSTERESIS_FACTOR = 0.9f;
/**
* Magic number for current version of cache file format.
*/
private static final int CACHE_MAGIC = 0x20150306;
/**
* 构造方法,在指定目录下创建缓存,并指定最大缓存大小
*
* @param rootDirectory 缓存根目录
* @param maxCacheSizeInBytes 最大缓存容量
*/
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
/**
* 构造方法,使用默认最大缓存容量进行创建,默认5M
*
* @param rootDirectory The root directory of the cache.
*/
public DiskBasedCache(File rootDirectory) {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
/**
* 清除磁盘上的所有缓存
*/
@Override
public synchronized void clear() {
File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
mEntries.clear();
mTotalSize = 0;
VolleyLog.d("Cache cleared.");
}
/**
* 查询缓存
*/
@Override
public synchronized Entry get(String key) {
//通过缓存Key查询缓存
CacheHeader entry = mEntries.get(key);
//没有查找到,返回null
if (entry == null) {
return null;
}
//查找到了,从磁盘上获取文件对象
File file = getFileForKey(key);
CountingInputStream cis = null;
try {
cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
CacheHeader.readHeader(cis); // eat header
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
//返回数据
return entry.toCacheEntry(data);
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
remove(key);
return null;
} catch (NegativeArraySizeException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
remove(key);
return null;
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException ioe) {
return null;
}
}
}
}
/**
* 初始化缓存目录,并扫描该目录下的缓存文件到内存中
*/
@Override
public synchronized void initialize() {
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
BufferedInputStream fis = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
CacheHeader entry = CacheHeader.readHeader(fis);
entry.size = file.length();
putEntry(entry.key, entry);
} catch (IOException e) {
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) {
}
}
}
}
/**
* Invalidates an entry in the cache.
*
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
@Override
public synchronized void invalidate(String key, boolean fullExpire) {
Entry entry = get(key);
if (entry != null) {
entry.softTtl = 0;
if (fullExpire) {
entry.ttl = 0;
}
put(key, entry);
}
}
/**
* 添加或更新缓存
*/
@Override
public synchronized void put(String key, Entry entry) {
//检查容量是否合理,不合理则删除
pruneIfNeeded(entry.data.length);
//获取缓存的文件
File file = getFileForKey(key);
try {
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos);
if (!success) {
fos.close();
VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
throw new IOException();
}
fos.write(entry.data);
fos.close();
//缓存数据
putEntry(key, e);
return;
} catch (IOException e) {
}
//缓存失败,则删除数据
boolean deleted = file.delete();
if (!deleted) {
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
/**
* Removes the specified key from the cache if it exists.
*/
@Override
public synchronized void remove(String key) {
boolean deleted = getFileForKey(key).delete();
removeEntry(key);
if (!deleted) {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
key, getFilenameForKey(key));
}
}
/**
* Creates a pseudo-unique filename for the specified cache key.
*
* @param key The key to generate a file name for.
* @return A pseudo-unique filename.
*/
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
/**
* 使用缓存Key从磁盘上找缓存文件,并返回
*/
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
/**
* 修剪缓存容量
*
* @param neededSpace 准备要添加的缓存容量大小
*/
private void pruneIfNeeded(int neededSpace) {
//当前容量 + 准备要添加的缓存容量,如果小于最大值,则还没有满容量,不做修剪
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
return;
}
if (VolleyLog.DEBUG) {
VolleyLog.v("Pruning old cache entries.");
}
long before = mTotalSize;
int prunedFiles = 0;
long startTime = SystemClock.elapsedRealtime();
Iterator> iterator = mEntries.entrySet().iterator();
//遍历LinkedHashMap,删掉头部数据
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
CacheHeader e = entry.getValue();
//删除缓存
boolean deleted = getFileForKey(e.key).delete();
if (deleted) {
//删除成功,更新缓存容量
mTotalSize -= e.size;
} else {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
e.key, getFilenameForKey(e.key));
}
iterator.remove();
prunedFiles++;
//修剪完成,跳出循环
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
break;
}
}
if (VolleyLog.DEBUG) {
VolleyLog.v("pruned %d files, %d bytes, %d ms",
prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);
}
}
/**
* 添加一个缓存
*/
private void putEntry(String key, CacheHeader entry) {
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
mEntries.put(key, entry);
}
/**
* 移除一个缓存
*/
private void removeEntry(String key) {
CacheHeader entry = mEntries.get(key);
if (entry != null) {
mTotalSize -= entry.size;
mEntries.remove(key);
}
}
//省略不重要的代码..
}
- NoCache
不缓存的实现,所有操作方法都是空实现
public class NoCache implements Cache {
@Override
public void clear() {
}
@Override
public Entry get(String key) {
return null;
}
@Override
public void put(String key, Entry entry) {
}
@Override
public void invalidate(String key, boolean fullExpire) {
}
@Override
public void remove(String key) {
}
@Override
public void initialize() {
}
}
鸿蒙上使用Volley
鸿蒙上没有HttpClient的API,所以不能使用安卓上的内置HttpClient,但Github上面有一个把内置HttpClient源码拿出来单独编译的HttpClient,所以我们可以直接用它,并且它的包名都成了cz.msebera.android
,改掉导包就好
以及JSONObject和JSONArray,Android上是把它们放到了android.jar,而鸿蒙中没有,所以要替换,这里我换成FastJson的JSONObject和JSONArray,修改一下JsonObjectRequest
和JsonArrayRequest
中的Json解析即可
下面给出所需要的HttpClient和FastJson依赖
dependencies {
//鸿蒙中没有JsonObject和JsonArray的类,Android中是在android.jar中,鸿蒙中没有,所以只能用FastJson
api 'com.alibaba:fastjson:1.2.76'
//因为鸿蒙没有HttpClient的API,所以单独引用
api 'cz.msebera.android:httpclient:4.5.8'
}
总结
首先通过Volley创建RequestQueue请求队列,内部有一个网络请求队列和缓存队列
创建1个缓存分发器,4个网络请求分发器,分发器实际是一个线程,不断死循环从队列中拿取请求出来处理
发出请求使用Network接口,而实际实现是BasicNetwork,内部会通过HttpStack发出请求,它有个2个实现类,HurlStack和HttpClientStack
请求成功判断请求是否需要缓存,如果需要则使用Cache接口进行缓存,而具体实现是DiskBasedCache,会把请求的响应、请求头等缓存到磁盘中。如果请求失败,则使用RetryPolicy缓存策略进行重试
最后请求成功或请求失败,会通过ResponseDelivery进行分发,把响应结果或错误发送到主线程,回调响应监听器或错误监听器