volley源码解析

来源:互联网 发布:新华电脑软件学校 编辑:程序博客网 时间:2024/06/10 01:47

总结一下Volley:

功能方面:

基于HttpURLConnection HttpClient;封装了UIL图片加载框架,支持图片加载;网络请求的排序,优先级处理;添加了网路缓存;

支持多级别取消网络请求;Activity和生命周期的联动(Activity结束时取消所有的网络请求)

可扩展性好支持HttpURLConnection HttpClient以及OKHttp

使用于数据量小但网络请求频繁的场景

缺点:不适合大数据量的网络请求


下面结合源码分析一下,先上官网的原理图


首先通过Volley构建出请求队列

  public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {        BasicNetwork network;        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {                network = new BasicNetwork(new HurlStack());            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                // At some point in the future we'll move our minSdkVersion past Froyo and can                // delete this fallback (along with all Apache HTTP code).                String userAgent = "volley/0";                try {                    String packageName = context.getPackageName();                    PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);                    userAgent = packageName + "/" + info.versionCode;                } catch (NameNotFoundException e) {                }                network = new BasicNetwork(                        new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));            }        } else {            network = new BasicNetwork(stack);        }        return newRequestQueue(context, network);    }
在创建请求队列时会先去创建一个Httpstack,这里从源码可以看到9之前采用的是HurlStack 继续查看HurlStack的源码发现在执行请求时会调用

openConnection(parsedUrl, request);获取得到网络连接在接下去发现调用的createConnection(url);这个方法,在此方法中我们发现最终调用的是
 (HttpURLConnection) url.openConnection();因此Android 9 之前Volley底层采用的是HttpURLConnection的网络请求方式、而9以后默认的采用的是
HttpClientStack而点进去查看发现其采用的是HttpClient的网络请求。 

 public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)            throws IOException, AuthFailureError {        String url = request.getUrl();        HashMap<String, String> map = new HashMap<>();        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.        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.");        }        if (!hasResponseBody(request.getMethod(), responseCode)) {            return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));        }        return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()),                connection.getContentLength(), inputStreamFromConnection(connection));    }



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);        // use caller-provided custom SslSocketFactory, if any, for HTTPS        if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);        }        return connection;    }

 protected HttpURLConnection createConnection(URL url) throws IOException {        HttpURLConnection connection = (HttpURLConnection) url.openConnection();        // Workaround for the M release HttpURLConnection not observing the        // HttpURLConnection.setFollowRedirects() property.        // https://code.google.com/p/android/issues/detail?id=194495        connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects());        return connection;    }

而同时Volley也是支持okhttp作为底层网络请求的。这就需要自己构建OKHttpStack  
方式一: 可以参照HurlStack的方式使用OkHttpStack extends HurlStac在重写
首先build.gradle中添加okhttp与okhttp-urlconnection的支持
   compile 'com.squareup.okhttp3:okhttp:3.2.0'    compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
createConnection方法
  protected HttpURLConnection createConnection(URL url) throws IOException {        OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient);        return okUrlFactory.open(url);    }

方法二

在eclipse中添加okhttp.jar支持此外定义okhttp重写performRequest方法

import java.io.IOException;import java.util.Map;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.ProtocolVersion;import org.apache.http.entity.BasicHttpEntity;import org.apache.http.message.BasicHeader;import org.apache.http.message.BasicHttpResponse;import org.apache.http.message.BasicStatusLine;import com.yulong.mobile.netroid.Request;import com.yulong.mobile.netroid.error.AuthFailureError;import android.annotation.SuppressLint;import android.content.Context;import android.os.Build;import android.webkit.WebSettings;import okhttp3.Call;import okhttp3.Headers;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.Protocol;import okhttp3.RequestBody;import okhttp3.Response;import okhttp3.ResponseBody;public class OkHttpStack implements HttpStack {    private final OkHttpClient mOkHttpClient;    private Context mContext;    public OkHttpStack(Context context,OkHttpClient okHttpClient) {        if (okHttpClient == null) {            throw new IllegalArgumentException("OkHttpClient can't be null");        }        this.mContext = context;        mOkHttpClient = okHttpClient;    }    @SuppressLint("NewApi")private  String getUserAgent() {        String userAgent = "";        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {            try {                userAgent = WebSettings.getDefaultUserAgent(mContext);            } catch (Exception e) {                userAgent = System.getProperty("http.agent");            }        } else {            userAgent = System.getProperty("http.agent");        }        StringBuffer sb = new StringBuffer();        for (int i = 0, length = userAgent.length(); i < length; i++) {            char c = userAgent.charAt(i);            if (c <= '\u001f' || c >= '\u007f') {                sb.append(String.format("\\u%04x", (int) c));            } else {                sb.append(c);            }        }        return sb.toString();    }    @Overridepublic HttpResponse performRequest(Request<?> request) throws IOException, AuthFailureError { OkHttpClient client = mOkHttpClient;        int timeoutMs = request.getTimeoutMs();        okhttp3.Request.Builder builder = new okhttp3.Request.Builder();        builder.url(request.getUrl());                Map<String, String> headers = request.getHeaders();                for (String name : headers.keySet()) {            builder.addHeader(name, headers.get(name));        }                builder.removeHeader("User-Agent").addHeader("User-Agent",getUserAgent()).build();        setConnectionParametersForRequest(builder, request);        okhttp3.Request okRequest = builder.build();        okhttp3.Call call = client.newCall(okRequest);        okhttp3.Response okresponse = call.execute();                BasicStatusLine responseStatus = new BasicStatusLine(                parseProtocol(okresponse.protocol()),                okresponse.code(),                okresponse.message()        );                BasicHttpResponse response = new BasicHttpResponse(responseStatus);        response.setEntity(entityFromOkHttpResponse(okresponse));        Headers responseHeaders = okresponse.headers();        int size = responseHeaders.size();        String name = null;        String value = null;        for (int i = 0; i < size; i++) {            name = responseHeaders.name(i);            if (name != null) {                response.addHeader(new BasicHeader(name, value));            }        }        return response;}private static HttpEntity entityFromOkHttpResponse(Response r) throws IOException {        BasicHttpEntity entity = new BasicHttpEntity();        ResponseBody body = r.body();        entity.setContent(body.byteStream());        entity.setContentLength(body.contentLength());        entity.setContentEncoding(r.header("Content-Encoding"));        if (body.contentType() != null) {            entity.setContentType(body.contentType().type());        }        return entity;    }  static void setConnectionParametersForRequest(okhttp3.Request.Builder builder, Request<?> request) throws IOException, AuthFailureError {        switch (request.getMethod()) {                       case Request.Method.GET:                builder.get();                break;            case Request.Method.DELETE:                builder.delete();                break;            case Request.Method.POST:                builder.post(createRequestBody(request));                break;            case Request.Method.PUT:                builder.put(createRequestBody(request));                break;            case Request.Method.HEAD:                builder.head();                break;            case Request.Method.OPTIONS:                builder.method("OPTIONS", null);                break;            case Request.Method.TRACE:                builder.method("TRACE", null);                break;            case Request.Method.PATCH:                builder.patch(createRequestBody(request));                break;            default:                throw new IllegalStateException("Unknown method type.");        }    }    private static RequestBody createRequestBody(Request r) throws AuthFailureError {        final byte[] body = r.getBody();        if (body == null) return null;        return RequestBody.create(MediaType.parse(r.getBodyContentType()), body);    }    private static ProtocolVersion parseProtocol(final Protocol p) {        switch (p) {            case HTTP_1_0:                return new ProtocolVersion("HTTP", 1, 0);            case HTTP_1_1:                return new ProtocolVersion("HTTP", 1, 1);            case SPDY_3:                return new ProtocolVersion("SPDY", 3, 1);            case HTTP_2:                return new ProtocolVersion("HTTP", 2, 0);        }        throw new IllegalAccessError("Unkwown protocol");    }    }



回到正题我们继续跟进去查看newRequestQueue(context, network)的源代码,发现构建RequestQueue之后会将其start()启动而在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();        }    }


volley构建了CacheDispatcher实例(CacheDispatcher extends Thread)然后调用了start方法,随后在for循环当中构建了NetworkDispatcher实例并也调用了start方法.默认情况下for循环会执行四次,这样也就是说后天会有五条线程在执行等待网络请求

  创建RequestQueue之后我们需要构建出Request然后将其add添加到RequestQueue当中

public <T> Request<T> add(Request<T> request) {        // Tag the request as belonging to this queue and add it to the set of current requests.        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);        }        // Process requests in the order they are added.        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // If the request is uncacheable, skip the cache queue and go straight to the network.        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        mCacheQueue.add(request);        return request;     }



   前面忘记交代RequestQueue当中会有两条优先级队列


PriorityBlockingQueue<Request<?>> mCacheQueue =new PriorityBlockingQueue<>();   
缓存队列PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<>(); 网络请求队列分别用来管理各自当中的request  
 从上面的add()当中的源码可知.volley会按照是否需要缓存调到多对应的队列当中,分别出队列交给CacheDispatcher实例以及NetworkDispatcher实例处理请求。
接下来先看看CacheDispatcher的处理方式其run方法
@Override    public void run() {        if (DEBUG) VolleyLog.v("start new dispatcher");        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        // Make a blocking call to initialize the cache.        mCache.initialize();        while (true) {            try {                // Get a request from the cache triage queue, blocking until                // at least one is available.                final Request<?> request = mCacheQueue.take();                request.addMarker("cache-queue-take");                // If the request has been canceled, don't bother dispatching it.                if (request.isCanceled()) {                    request.finish("cache-discard-canceled");                    continue;                }                // Attempt to retrieve this item from cache.                Cache.Entry entry = mCache.get(request.getCacheKey());                if (entry == null) {                    request.addMarker("cache-miss");                    // Cache miss; send off to the network dispatcher.                    if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                        mNetworkQueue.put(request);                    }                    continue;                }                // If it is completely expired, just send it to the network.                if (entry.isExpired()) {                    request.addMarker("cache-hit-expired");                    request.setCacheEntry(entry);                    if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                        mNetworkQueue.put(request);                    }                    continue;                }                // We have a cache hit; parse its data for delivery back to the request.                request.addMarker("cache-hit");                Response<?> response = request.parseNetworkResponse(                        new NetworkResponse(entry.data, entry.responseHeaders));                request.addMarker("cache-hit-parsed");                if (!entry.refreshNeeded()) {                    // Completely unexpired cache hit. Just deliver the response.                    mDelivery.postResponse(request, response);                } else {                    // Soft-expired cache hit. We can deliver the cached response,                    // but we need to also send the request to the network for                    // refreshing.                    request.addMarker("cache-hit-refresh-needed");                    request.setCacheEntry(entry);                    // Mark the response as intermediate.                    response.intermediate = true;                    if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                        // Post the intermediate response back to the user and have                        // the delivery then forward the request along to the network.                        mDelivery.postResponse(request, response, new Runnable() {                            @Override                            public void run() {                                try {                                    mNetworkQueue.put(request);                                } catch (InterruptedException e) {                                    // Restore the interrupted status                                    Thread.currentThread().interrupt();                                }                            }                        });                    } else {                        // request has been added to list of waiting requests                        // to receive the network response from the first request once it returns.                        mDelivery.postResponse(request, response);                    }                }            } catch (InterruptedException e) {                // We may have been interrupted because it was time to quit.                if (mQuit) {                    return;                }            }        }    }

可以看到缓存线程当中有一个while循环一直在处理request请求,先会去cache当中获取是否存在,并查看是否数据是否已经过期,否则重新添加到networkQueue(
这个NetworkQueue就是在RequestQueue start()执行构建CacheDispatcher时创建并传入的)如果存在并数据有效调用
mDelivery.postResponse(request, response);方法将数据返回mDelivery即在RequestQueue创建时构建的new ExecutorDelivery(new Handler(Looper.getMainLooper()))
查看ExecutorDelivery 的postResponse方法
ExecutorDeliveryd

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

查看ExecutorDelivery 的postResponse方法

 

public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {        request.markDelivered();        request.addMarker("post-response");        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));    }  private 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 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();            }       }    }

从以上可以看出,最后调用handle.post方法将结果返回到主线程   通过mRequest.deliverResponse(mResponse.result);回调到了request的方法  而request的抽象方法deliverResponse在StringRequest等都会有实现  

可以看到最终还是通过Handler.post的方式回到而来主线程,而Handler则是在RequestQueue构造过程中new出Handler传递给ExecutorDelivery的。看一下RequestQueue当中

的构造方法

 public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

new Handler(Loop.getMainLooper())RequestQueue实例创建时候还在主线程。从这这种就构建好ExecutorDelviery实例

CacheDispatcher中会用传进取得Cache实现类DiskBasedCache来实现缓存

DiskBasedCache中有

private final Map<String, CacheHeader> mEntries =        new LinkedHashMap<String, CacheHeader>(16, .75f, true);
作为缓存数据同时会将data数据写入磁盘当中
public synchronized void put(String key, Entry entry) {        pruneIfNeeded(entry.data.length);        File file = getFileForKey(key);        try {            BufferedOutputStream fos = new BufferedOutputStream(createOutputStream(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());        }    }



 @Override    protected void deliverResponse(String response) {        if (mListener != null) {            mListener.onResponse(response);        }    }
最后真正会掉到了request传来的listener
这个具体的deliverResponse()方法当然最后可以自己具体的做实现,同样错误时一样的道理
   同理在查看NetworkDispatch

@Override    public void run() {        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        while (true) {            long startTimeMs = SystemClock.elapsedRealtime();            Request<?> request;            try {                // Take a request from the queue.                request = mQueue.take();            } catch (InterruptedException e) {                // We may have been interrupted because it was time to quit.                if (mQuit) {                    return;                }                continue;            }            try {                request.addMarker("network-queue-take");                // If the request was cancelled already, do not perform the                // network request.                if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    continue;                }                addTrafficStatsTag(request);                // Perform the network request.                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker("network-http-complete");                // If the server returned 304 AND we delivered a response already,                // we're done -- don't deliver a second identical response.                if (networkResponse.notModified && request.hasHadResponseDelivered()) {                    request.finish("not-modified");                    continue;                }                // Parse the response here on the worker thread.                Response<?> response = request.parseNetworkResponse(networkResponse);                request.addMarker("network-parse-complete");                // Write to cache if applicable.                // TODO: Only update cache metadata instead of entire record for 304s.                if (request.shouldCache() && response.cacheEntry != null) {                    mCache.put(request.getCacheKey(), response.cacheEntry);                    request.addMarker("network-cache-written");                }                // Post the response back.                request.markDelivered();                mDelivery.postResponse(request, response);            } catch (VolleyError volleyError) {                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                parseAndDeliverNetworkError(request, volleyError);            } catch (Exception e) {                VolleyLog.e(e, "Unhandled exception %s", e.toString());                VolleyError volleyError = new VolleyError(e);                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                mDelivery.postError(request, volleyError);            }        }    }



调用BasicNetwork中的performRequest执行网络请求可以看到
responseContents = inputStreamToBytes(inputStream, httpResponse.getContentLength());Volley使用byte[]接收网络返回的数据的。所以不建议有大数据量返回时
使用volley作为网络请求

@Override    public NetworkResponse performRequest(Request<?> request) throws VolleyError {        long requestStart = SystemClock.elapsedRealtime();        while (true) {            HttpResponse httpResponse = null;            byte[] responseContents = null;            List<Header> responseHeaders = Collections.emptyList();            try {                // Gather headers.                Map<String, String> additionalRequestHeaders =                        getCacheHeaders(request.getCacheEntry());                httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);                int statusCode = httpResponse.getStatusCode();                responseHeaders = httpResponse.getHeaders();                // Handle cache validation.                if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {                    Entry entry = request.getCacheEntry();                    if (entry == null) {                        return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null,                                responseHeaders, true,                                SystemClock.elapsedRealtime() - requestStart);                    }                    // Combine cached and response headers so the response will be complete.                    List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);                    return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, entry.data,                            combinedHeaders, true,                            SystemClock.elapsedRealtime() - requestStart);                }                // Some responses such as 204s do not have content.  We must check.                InputStream inputStream = httpResponse.getContent();                if (inputStream != null) {                  responseContents =                          inputStreamToBytes(inputStream, httpResponse.getContentLength());                } 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, statusCode);                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 (MalformedURLException e) {                throw new RuntimeException("Bad URL " + request.getUrl(), e);            } catch (IOException e) {                int statusCode;                if (httpResponse != null) {                    statusCode = httpResponse.getStatusCode();                } else {                    throw new NoConnectionError(e);                }                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());                NetworkResponse networkResponse;                if (responseContents != null) {                    networkResponse = new NetworkResponse(statusCode, responseContents,                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);                    if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED ||                            statusCode == HttpURLConnection.HTTP_FORBIDDEN) {                        attemptRetryOnException("auth",                                request, new AuthFailureError(networkResponse));                    } else if (statusCode >= 400 && statusCode <= 499) {                        // Don't retry other client errors.                        throw new ClientError(networkResponse);                    } else if (statusCode >= 500 && statusCode <= 599) {                        if (request.shouldRetryServerErrors()) {                            attemptRetryOnException("server",                                    request, new ServerError(networkResponse));                        } else {                            throw new ServerError(networkResponse);                        }                    } else {                        // 3xx? No reason to retry.                        throw new ServerError(networkResponse);                    }                } else {                    attemptRetryOnException("network", request, new NetworkError());                }            }        }    }


mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
即调用的是刚开始RequestQueue构建时传入的HttpStack实例的executeRequest方法。所以在HttpStack的构建当中可以做出可以合适的选择,目前来说volley是支持
HttpURLConnection,HttpClient以及OkHttp的
从源码看Volley还对网络错误做了相应的处理
多级别取消:

request.cancel();以及requestQueue.cancalAll()在BasicNetwork的performRequest方法中有做request是否取消的判断

   if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    request.notifyListenerResponseNotUsable();                    continue;                }

同样requestQueue当中有对请求队列当中取消请求的方法

 /**     * Cancels all requests in this queue with the given tag. Tag must be non-null     * and equality is by identity.     */    public void cancelAll(final Object 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;            }        });    }

volley与activity的联动在activity的onStop方法当中添加如下代码即可

@Override    protected void onStop() {        super.onStop();        //通过Tag标签取消请求队列中对应的全部请求        MyApplication.getHttpQueue().cancelAll("abcPost");    }

volley配置重新请求retry

stringRequest.setRetryPolicy(new DefaultRetryPolicy(timeout * 1000, retrytime, 0.0f));
具体查看volley重试
Volley的缓存实现
Request当中有对应的mShouldCache用来决定是否需要缓存
而在向RequestQueue当中添加request的时候 会按照request的mShouldCache的值来判断是加入到cacheQueue还是networkQueue
  public <T> Request<T> add(Request<T> request) {        // Tag the request as belonging to this queue and add it to the set of current requests.        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);        }        // Process requests in the order they are added.        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // If the request is uncacheable, skip the cache queue and go straight to the network.        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        mCacheQueue.add(request);        return request;     }

而上面知道了 start的时候会调用对应的Dispather执行对应的操作
Volley的多级别退出以及与Fragment、Activity联动
在RqeustQueue当中
    public void cancelAll(RequestFilter filter) {        synchronized (mCurrentRequests) {            for (Request<?> request : mCurrentRequests) {                if (filter.apply(request)) {                    request.cancel();                }            }        }    }

在request当中
public void cancel() {        mCanceled = true;    }
最后在NetworkDiapatcher中
       if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    request.notifyListenerResponseNotUsable();                    continue;                }

而在CacheDispatcher当中
     if (request.isCanceled()) {                    request.finish("cache-discard-canceled");                    continue;                }

所以可以在Activity或者Fragment当中的onDestroy当中调用cancel方法即可
volley重请求实现RetryPolicy  然后通过
stringRequest.setRetryPolicy(。。。);设置
Volley顺序请求
request当中有sequence字段
为什么说Volley适合频繁但是数据量小的网络请求而不适合大数据量的网络请求
在BasicNetWork的performRequest当中会发现
byte[] responseContents = null;
...
responseContents =                          inputStreamToBytes(inputStream, httpResponse.getContentLength());


这就是说volley在做网络请求时使用的是byte[]数组进行的接收而不是使用的stream,这样当遇到大文件时容易导致内存溢出
接下来对应的流转化成byte[]
protected final ByteArrayPool mPool;
 private byte[] inputStreamToBytes(InputStream in, int contentLength)            throws IOException, ServerError {        PoolingByteArrayOutputStream bytes =                new PoolingByteArrayOutputStream(mPool, contentLength);        byte[] buffer = null;        try {            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".                if (in != null) {                    in.close();                }            } catch (IOException e) {                // This can happen if there was an exception above that left the stream in                // an invalid state.                VolleyLog.v("Error occurred when closing InputStream");            }            mPool.returnBuf(buffer);            bytes.close();        }    }

ByteArrayPool 因为ByteArrayPool的存在而适合
网络请求得到数据,就需要在内存开辟一个控件来存储数据;当网络请求频繁的时候,我们先要在堆中开辟存储空间,显示以后再合适的情况下有GC回收,这样频繁的时候GC的
工作量会很大,然后导致客户端有压力,于是有了ByteArrayPool字节数据缓存池
ByteArrayPool会有mPool.getBuf(1024);以及mPool.returnBuf(buffer);来完成从缓存区取和还的过程
http://blog.csdn.net/ss1168805219/article/details/52039444
最后理一下Volley的整个执行过程 NetworkDiapatcher 以及CacheDiapatcher两个线程进行网络请求的处理,NetworkDispatcher调用BasicNetwork(implements Network)的
performRequest方法这之中调用httpStack的performRequst返回响应数据经BasicNetwork处理(这种中又byte[]接收ByteArrayPool加大了内存使用效率)请求状态码并返回
对应的响应数据给NetworkDispatcher。之后NetworkDispatcher调用Request的parseNetworkResponse返回Response。最后通过ExecutorDelivery的postResponse由Handler的
post回到主线程。调用request的deliverResponse方法调用Listener来进行回调。走完整个流程与NetworkDispatcher不同的是CacheDispatcher仅仅只是有缓存直接返回Response
画了一个草图

 
原创粉丝点击