Volley源码解析二

NetworkDispatcher

Volley源码解析一说到如果Request加入网络请求队列的情况有三种

  1. Request不需要缓存
  2. 缓存内容已经过期
  3. Ttl 不过期,但是softTtl 已经超出了当前的时间
    那么4个NetworkDispatcher线程中的其中一个线程就会从阻塞队列取出Request脱离阻塞,代码如下
//NetworkDispatcher.java
public void run() {
        Process.setThreadPriority(10);

        while(true) {
            Request request;
            while(true) {
                try {
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var4) {
                    if (this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                // 如果请求在加入到网络请求队列前就已经取消了,结束这个请求
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                } else {
                    if (VERSION.SDK_INT >= 14) {
                        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                    }
				//执行网络请求逻辑mNetwork为BasicNetwork(new HurlStack())该方法要么返回要么就抛异常
				//NetworkDispatcher -> BasicNetwork -> AdaptedHttpStack -> HurlStack
                    NetworkResponse networkResponse = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    //如果返回304并且该响应已经被分发,不会再次分发同样的响应,
                    //主要用于哪些Ttl没超时,而softTtl超时需要刷新请求
                    //再次请求,对于这种请求返回304,不需要再次分发了,所以对于这种请求如果这次请求返回304,那么Request.parseNetworkResponse会被回调两次
                    if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                    //解析响应
                        Response<?> response = request.parseNetworkResponse(networkResponse);
                        request.addMarker("network-parse-complete");
                        //如果请求需要缓存那么将响应缓存
                        if (request.shouldCache() && response.cacheEntry != null) {
                            this.mCache.put(request.getCacheKey(), response.cacheEntry);
                            request.addMarker("network-cache-written");
                        }
						//标记请求已经被分发,其实ExecutorDelivery内部postResponse也会调用request.markDelivered()
                        request.markDelivered();
                   
                        this.mDelivery.postResponse(request, response);
                    }
                }
            } catch (VolleyError var5) {
                this.parseAndDeliverNetworkError(request, var5);
            } catch (Exception var6) {
                VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
                this.mDelivery.postError(request, new VolleyError(var6));
            }
        }
    }
  • 先看下 mNetwork.performRequest
//BasicNetwork.java
 public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while(true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            HashMap responseHeaders = new HashMap();

            try {
            
                Map<String, String> headers = new HashMap();
                //这里的request.getCacheEntry就是上一篇文章中的如果缓存过期了
            	//(包括Ttl超时或者softTtl超时)就设置request.setCacheEntry(entry)
                this.addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = this.mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                Map<String, String> responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                //处理缓存确认,如果是304,就直接使用缓存的内容
                if (statusCode == 304) {
                    return new NetworkResponse(304, request.getCacheEntry().data, responseHeaders, true);
                }

                byte[] responseContents;
                             // 一些响应比如204、304、HEAD请求没有响应体,必须检查它,防止其被下面的catch代码块判断为部非手动抛出的IO异常
                if (httpResponse.getEntity() != null) {
                    responseContents = this.entityToBytes(httpResponse.getEntity());
                } else {
                //添加一个空数组来代表无响应体的响应
                    responseContents = new byte[0];
                }
				// 如果请求超过3秒就打印日志
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                this.logSlowRequests(requestLifetime, request, responseContents, statusLine);
                if (statusCode >= 200 && statusCode <= 299) {
                    return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
                }

                throw new IOException();
            } catch (SocketTimeoutException var12) {
            // 重试机制
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException var13) {
            // 重试机制
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException var14) {
                throw new RuntimeException("Bad URL " + request.getUrl(), var14);
            } catch (IOException var15) {
                int statusCode = false;
                NetworkResponse networkResponse = null;
                if (httpResponse == null) {
                    throw new NoConnectionError(var15);
                }

                int statusCode = httpResponse.getStatusLine().getStatusCode();
                VolleyLog.e("Unexpected response code %d for %s", new Object[]{statusCode, request.getUrl()});
                if (responseContents == null) {
                    throw new NetworkError(networkResponse);
                }

                networkResponse = new NetworkResponse(statusCode, (byte[])responseContents, responseHeaders, false);
                if (statusCode != 401 && statusCode != 403) {
                    throw new ServerError(networkResponse);
                }
				//如果是401或者是403就调用RetryPolicy.retry进行重试
                attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
            }
        }
    }

可以看到通过mHttpStack.performRequest方法返回响应的数据,其实mHttpStack就是HurlStack和HttpClientStack的父类。回到NetworkDispatcher请求网络后,会将响应结果存在缓存中,并调用下面的这段代码

this.mDelivery.postResponse(request, response);
 
 @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

ResponseDeliveryRunnable里面做了什么

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

上面的代码可以看到如果响应成功将执行mRequest.deliverResponse(mResponse.result),响应失败的话,就执行mRequest.deliverError(mResponse.error)。接下来看下Request的deliverResponse都做了什么,因为Request子类比较多,这里拿StringRequest来做参考。

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

错误的回调在父类Request中可以找到

public void deliverError(VolleyError error) {
        if (this.mErrorListener != null) {
            this.mErrorListener.onErrorResponse(error);
        }

    }

拿到响应结果之后,如果请求成功则回调onResponse,如果请求失败则回调onErrorReponse,这个流程就是这样了。

Volley 的日志管理

源码中好多地方都看到了request.addMarker()来探究一下

 public void addMarker(String tag) {
        if (MarkerLog.ENABLED) {
            mEventLog.add(tag, Thread.currentThread().getId());
        } else if (mRequestBirthTime == 0) {
            mRequestBirthTime = SystemClock.elapsedRealtime();
        }
    }


 public synchronized void add(String name, long threadId) {
            if (this.mFinished) {
                throw new IllegalStateException("Marker added to finished log");
            } else {
                this.mMarkers.add(new VolleyLog.MarkerLog.Marker(name, threadId, SystemClock.elapsedRealtime()));
            }
        }
//到这里就把日志添加到mMarkers里面去了,那么什么时候答应日志呢,在Request.finish()


    void finish(final String tag) {
    // 从请求队列中将自己移除
        if (this.mRequestQueue != null) {
            this.mRequestQueue.finish(this);
        }

        final long threadId;
        if (MarkerLog.ENABLED) {
            threadId = Thread.currentThread().getId();
            if (Looper.myLooper() != Looper.getMainLooper()) {
            //在主线程中调用
                Handler mainThread = new Handler(Looper.getMainLooper());
                mainThread.post(new Runnable() {
                    public void run() {
                        Request.this.mEventLog.add(tag, threadId);
                        Request.this.mEventLog.finish(this.toString());
                    }
                });
                return;
            }

            this.mEventLog.add(tag, threadId);
            this.mEventLog.finish(this.toString());
        } else {
            threadId = SystemClock.elapsedRealtime() - this.mRequestBirthTime;
            if (threadId >= 3000L) {
                VolleyLog.d("%d ms: %s", new Object[]{threadId, this.toString()});
            }
        }

    }

// VolleyLog$MarkerLog,finish方法内部进行打印
 public synchronized void finish(String header) {
            this.mFinished = true;
            long duration = this.getTotalDuration();
            if (duration > 0L) {
                long prevTime = ((VolleyLog.MarkerLog.Marker)this.mMarkers.get(0)).time;
                VolleyLog.d("(%-4d ms) %s", duration, header);

                long thisTime;
                for(Iterator var7 = this.mMarkers.iterator(); var7.hasNext(); prevTime = thisTime) {
                    VolleyLog.MarkerLog.Marker marker = (VolleyLog.MarkerLog.Marker)var7.next();
                    thisTime = marker.time;
                    VolleyLog.d("(+%-4d) [%2d] %s", thisTime - prevTime, marker.thread, marker.name);
                }

            }
        }
  private long getTotalDuration() {
            if (this.mMarkers.size() == 0) {
                return 0L;
            } else {
            最后一个减第一个
                long first = ((VolleyLog.MarkerLog.Marker)this.mMarkers.get(0)).time;
                long last = ((VolleyLog.MarkerLog.Marker)this.mMarkers.get(this.mMarkers.size() - 1)).time;
                return last - first;
            }
        }
  • 重试机制,可以看到连接超时,地址格式错误等都会调用attemptRetryOnException()方法进行重试
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 var6) {
            request.addMarker(String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
            throw var6;
        }

        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }
总结
  1. 由于Volley内部使用了ByteArrayPool避免每次都去新建字节数组对象,所以适用于处理高频率的请求,同时由于其将所有响应通过字节数组输出流读入了内存所以不适合进行大文件的下载,否则容易造成OOM。
  2. 如果需要发送Post请求可以继承Request,重写getParams方法。
  3. Volley的Request可以取消,一旦取消就不会回调deliverResponse、deliverError,前提是调用cancel的线程必须是主线程(因为ResponseDeliveryRunnable是运行在主线程的)。
  4. 可以通过实现RetryPolicy来控制每次请求的超时时间,和最大重试次数。
  5. 如果需要给Request加上自定义的请求头可以重写Request.getHeaders。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值