Volley首先就看Volley类,也主是新建网络请求队列:
/**
* 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);
// File cacheDir = FileUtil.getVolleyDir();
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 ;
}
在这里要重点介绍下四个类,一个是HurlStack也是SDK>=9的真正网络请求实现,一个是BasicNetwork封装了网络请求,主要处理请求错误的情况,DiskBasedCache是缓存类,Volley默认是存储缓存的,最后一个是RequestQueue,也就是请求队列。
HurlStack:
/**
* @param
urlRewriter
Rewriter to use for request URLs
* @param
sslSocketFactory
SSL factory to use for HTTPS connections
*/
public
HurlStack(UrlRewriter urlRewriter
,
SSLSocketFactory sslSocketFactory) {
mUrlRewriter
= urlRewriter
;
mSslSocketFactory
= sslSocketFactory
;
}
从说明里可以知道:
UrlRewriter是重写url,需要实现接口;SSLSocketFactory是用于HTTPS的SSL;
/**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected
HttpURLConnection
createConnection
(URL url)
throws
IOException {
return
(HttpURLConnection) url.openConnection()
;
}
返回HttpURLConnection,从这里可以知道,Volley的网络请求实现最终是通过HttpURLConnection来实现的。
/**
* Opens an {@link HttpURLConnection} with parameters.
* @param
url
* @return an open connection
* @throws IOException
*/
private
HttpURLConnection
openConnection
(URL url
,
Request> request)
throws
IOException {
HttpURLConnection connection = createConnection(url)
;
// int timeoutMs = request.getTimeoutMs();
int
timeoutMs =
10
*
1000
;
connection.setConnectTimeout(timeoutMs)
;
connection.setReadTimeout(timeoutMs)
;
connection.setUseCaches(
false
)
;
connection.setDoInput(
true
)
;
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if
(
"https"
.equals(url.getProtocol()) &&
mSslSocketFactory
!=
null
) {
((HttpsURLConnection)connection).setSSLSocketFactory(
mSslSocketFactory
)
;
}
return
connection
;
}
对HttpURLConnection设置超时时间和不使用缓存等参数;
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()
;
}
}
对HttpURLConnection设置output,这个要看情况,如果这个请求有正文就需要,一般是post请求,put请求,patch请求。
static void
setConnectionParametersForRequest
(HttpURLConnection connection
,
Request> request)
throws
IOException
,
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.
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"
)
;
connection.addRequestProperty(
HEADER_CONTENT_TYPE
,
request.getPostBodyContentType())
;
DataOutputStream out =
new
DataOutputStream(connection.getOutputStream())
;
out.write(postBody)
;
out.close()
;
}
break;
case
Method.
GET
:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
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
:
addBodyIfExists (connection
,
request)
;
connection.setRequestMethod(
"PATCH"
)
;
break;
default
:
throw new
IllegalStateException(
"Unknown method type."
)
;
}
}
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* @param
connection
* @return an HttpEntity populated with data from
connection
.
*/
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
;
}
在这里将调用connection.getInputStream();这时才会将请求发送给后台。
设置方法参数,如前面所说,如果是post请求,需要先把正文写到cache。
@Override
public
HttpResponse
performRequest
(Request> request
,
Map
,
String> additionalHeaders)
throws
IOException
,
AuthFailureError {
String url = request.getUrl()
;
HashMap
,
String> map =
new
HashMap
,
String>()
;
map.putAll(request.getHeaders())
;
map.putAll(additionalHeaders)
;
if
(
mUrlRewriter
!=
null
) {
String rewritten =
mUrlRewriter
.rewriteUrl(url)
;
if
(rewritten ==
null
) {
throw new
IOException(
"URL blocked by rewriter: "
+ url)
;
}
url = rewritten
;
}
URL parsedUrl =
new
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 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new
IOException(
"Could not retrieve response code from HttpUrlConnection."
)
;
}
StatusLine responseStatus =
new
BasicStatusLine(protocolVersion
,
connection.getResponseCode()
,
connection.getResponseMessage())
;
BasicHttpResponse response =
new
BasicHttpResponse(responseStatus)
;
response.setEntity( entityFromConnection(connection))
;
for
(Entry
,
List> header : connection.getHeaderFields().entrySet()) {
if
(header.getKey() !=
null
) {
Header h =
new
BasicHeader(header.getKey()
,
header.getValue().get(
0
))
;
response.addHeader(h)
;
}
}
return
response
;
}
这个函数实现的是网络请求的各种参数设置跟实现网络请求,并返回响应。
BasicNetWork:
这个类主要做了两件事,处理网络请求,处理网络响应。
@Override
public
NetworkResponse
performRequest
(Request> request)
throws
VolleyError {
long
requestStart = SystemClock.elapsedRealtime()
;
while
(
true
) {
HttpResponse httpResponse =
null;
byte
[] responseContents =
null;
Map
,
String> responseHeaders =
new
HashMap
,
String>()
;
try
{
// Gather headers.
Map
,
String> headers =
new
HashMap
,
String>()
;
addCacheHeaders(headers
,
request.getCacheEntry())
;
httpResponse =
mHttpStack
.performRequest(request
,
headers)
;
StatusLine statusLine = httpResponse.getStatusLine()
;
int
statusCode = statusLine.getStatusCode()
;
responseHeaders = convertHeaders(httpResponse.getAllHeaders())
;
// Handle cache validation.
if
(statusCode == HttpStatus.
SC_NOT_MODIFIED
) {
return new
NetworkResponse(HttpStatus.
SC_NOT_MODIFIED
,
request.getCacheEntry().
data
,
responseHeaders
, true
)
;
}
// Some responses such as 204s do not have content. We must check.
if
(httpResponse.getEntity() !=
null
) {
responseContents = entityToBytes(httpResponse.getEntity())
;
}
else
{
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents =
new byte
[
0
]
;
}
// if the request is slow, log it.
long
requestLifetime = SystemClock.elapsedRealtime() - requestStart
;
logSlowRequests (requestLifetime
,
request
,
responseContents
,
statusLine)
;
if
(statusCode <
200
|| statusCode >
299
) {
throw new
IOException()
;
}
return new
NetworkResponse(statusCode
,
responseContents
,
responseHeaders
, false
)
;
}
catch
(SocketTimeoutException e) {
attemptRetryOnException (
"socket"
,
request
, new
TimeoutError())
;
e.printStackTrace()
;
}
catch
(ConnectTimeoutException e) {
attemptRetryOnException (
"connection"
,
request
, new
TimeoutError())
;
e.printStackTrace()
;
}
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)
;
}
VolleyLog.e(
"Unexpected response code %d for %s"
,
statusCode
,
request.getUrl())
;
if
(responseContents !=
null
) {
networkResponse =
new
NetworkResponse(statusCode
,
responseContents
,
responseHeaders
, false
)
;
if
(statusCode == HttpStatus.
SC_UNAUTHORIZED
||
statusCode == HttpStatus.
SC_FORBIDDEN
) {
attemptRetryOnException(
"auth"
,
request
, new
AuthFailureError(networkResponse))
;
}
else
{
//
TODO: Only throw ServerError for 5xx status codes.
throw new
ServerError(networkResponse)
;
}
}
else
{
throw new
NetworkError(networkResponse)
;
}
}
}
}
如果响应内容是没更改过,就直接返回,如果不是且有内容,则存储:
/** Reads the contents of HttpEntity into a 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()
;
}
}