Volley源码解析(一):网络请求内容

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() ;
   
}
}

你可能感兴趣的:(安卓)