zoukankan      html  css  js  c++  java
  • volley 笔记

    volley 阅读笔记:
    ====================================================================================================
    Volley 类
    只是提供了 newRequestQueue 方法。
    在 newRequestQueue 方法中,根据版本自动选择网络实现方式:
    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 :
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1) {
    // No maximum size specified
    queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    } else {
    // Disk cache size specified
    queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }
    queue.start();
    ====================================================================================================
    Network 接口
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;
    这个方法主要是调用了 HttpStack 的 performRequest 方法,其他的就是处理了请求头和响应头,以及返回码的处理。
    如 304, 301, 302 等。

    里面的重定向是通过重试策略来处理的:
    if (statusCode < 200 || statusCode > 299) {
    throw new IOException();
    }

    然后捕获异常,分别处理:
    } catch (IOException e) {
    ...
    if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
    statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
    attemptRetryOnException("redirect",
    request, new RedirectError(networkResponse));
    }
    ...
    }

    实现类为BasicNetWork
    ====================================================================================================
    RequestQueue 类
    // 这个 map 里面存储的是 重复请求
    // 请求的缓存 key 相同,则放入缓存 key 对应的队列,如果没有队列则新建一个队列
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
    new HashMap<String, Queue<Request<?>>>();
    核心方法:
    public void start() {}
    将 CacheDispatcher 和 NetworkDispatcher 给启动起来

    public void cancelAll(final Object tag) {}
    取消 tag 匹配的请求,方法里面用到了过滤器的方法 -- public void cancelAll(RequestFilter filter) {} --

    public <T> Request<T> add(Request<T> request) {}
    将 Request 添加到 RequestQueue 中

    添加过程:
    1.先添加到集合(mCurrentRequests,这个集合保存队列所有请求)中,
    synchronized (mCurrentRequests) {
    mCurrentRequests.add(request);
    }
    2.判断该请求是否缓存,不缓存则直接添加到网络请求队列
    if (!request.shouldCache()) {
    mNetworkQueue.add(request);
    return request;
    }
    3.需要缓存,从等待队列里面找出缓存key相同的请求,放入对应的等待队列中
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
    new HashMap<String, Queue<Request<?>>>();
    mWaitingRequests 是一个url,对应着一个队列。说明有一个请求重复执行多次。
    String cacheKey = request.getCacheKey();
    if (mWaitingRequests.containsKey(cacheKey)) {
    // There is already a request in flight. Queue up.
    Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
    if (stagedRequests == null) {
    stagedRequests = new LinkedList<Request<?>>();
    }
    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;
    ====================================================================================================
    HttpStack 接口
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
    throws IOException, AuthFailureError;
    这个方法实际上就是我们平时写的网络请求,只不过考虑的细节更多。

    有两个实现类,HttpClientStack 和 HurlStack
    有两个实现是因为:Prior to Gingerbread, HttpUrlConnection was unreliable.
    HurlStack是封装的HttpUrlConnection,google团队会将优化精力放在这个类上面,推荐使用这个。
    ====================================================================================================
    CacheDispatcher 类
    CacheDispatcher 继承 Thread

    看 run 方法
    public void run() {}
    1.设置线程优先级:Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    2.初始化缓存 mCache.initialize();
    3.开启一个死循环 while (true) { ... }
    4.取出一个请求,如果该请求被取消,则 finish 这个请求,继续下一个循环
    if (request.isCanceled()) {
    request.finish("cache-discard-canceled");
    continue;
    }
    5.未取消,则通过请求缓存的 key 值取出相应的缓存:Cache.Entry entry = mCache.get(request.getCacheKey());
    6.取出的缓存为 null,将该请求放入网络请求队列中去执行,继续下一个循环:
    if (entry == null) {
    request.addMarker("cache-miss");
    // Cache miss; send off to the network dispatcher.
    mNetworkQueue.put(request);
    continue;
    }
    7.缓存不为 null,但是过期了,将请求放入网络请求队列中执行,继续下一个循环:
    if (entry.isExpired()) {
    request.addMarker("cache-hit-expired");
    request.setCacheEntry(entry);
    mNetworkQueue.put(request);
    continue;
    }
    8.将缓存封装成响应结果:
    Response<?> response = request.parseNetworkResponse(
    new NetworkResponse(entry.data, entry.responseHeaders));
    9.如果缓存不需要更新,直接将请求响应分发出去(一般是主线程)
    mDelivery.postResponse(request, response);
    10.缓存需要更新,将请求放入网络请求队列
    // 设置缓存
    request.setCacheEntry(entry);

    // Mark the response as intermediate.
    // 设置这个玩意,表示在分发请求响应之后不会finish这个请求,因为这个缓存需要更新
    // 这个请求会被放到网络请求队列中执行
    response.intermediate = true;

    // Post the intermediate response back to the user and have
    // the delivery then forward the request along to the network.
    final Request<?> finalRequest = request;
    mDelivery.postResponse(request, response, new Runnable() {
    @Override
    public void run() {
    try {
    mNetworkQueue.put(finalRequest);
    } catch (InterruptedException e) {
    // Not much we can do about this.
    }
    }
    });
    ====================================================================================================
    NetworkDispatcher 类
    NetworkDispatcher 继承 Thread
    和 CacheDispatcher 基本差不多
    1.遇到304,如果请求响应结果已经被分发出去了,结束这个请求,继续下一个循环:
    if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    request.finish("not-modified");
    continue;
    }
    2.解析网络请求:
    Response<?> response = request.parseNetworkResponse(networkResponse);
    具体的解析方式,与请求有关,如果是 JsonRequest,就解析成 Json。
    3.如果请求需要缓存,并且相应中有数据,则储存缓存
    if (request.shouldCache() && response.cacheEntry != null) {
    mCache.put(request.getCacheKey(), response.cacheEntry);
    request.addMarker("network-cache-written");
    }
    4.记录响应已经被分发出去,分发响应,出错则分发错误:
    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);
    }
    ====================================================================================================
    ResponseDelivery 接口
    /**
    * Parses a response from the network or cache and delivers it.
    */
    public void postResponse(Request<?> request, Response<?> response);

    /**
    * Parses a response from the network or cache and delivers it. The provided
    * Runnable will be executed after delivery.
    */
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /**
    * Posts an error for the given request.
    */
    public void postError(Request<?> request, VolleyError error);
    提供了分发请求响应结果和错误的方法。

    ExecutorDelivery 为实现类
    实际上是利用 Handler post 了一个 Runnable :
    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
    this(cache, network, threadPoolSize,
    // 获取主线程的Handler
    new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
    // 实例化Executor
    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);
    }
    };
    }
    // 分发请求响应
    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
    request.markDelivered();
    request.addMarker("post-response");
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }
    // 请求被取消,结束请求,返回
    // 响应成功,分发请求响应
    // 响应失败,分发错误
    // 响应执行完,结束请求,未执行完(mResponse.intermediate)不做操作
    // 执行runnable里面的代码
    @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();
    }
    }
    ====================================================================================================
    RequestQueue 和 CacheDispatcher 和 NetworkDispatcher

    public RequestQueue(Cache cache, Network network, int threadPoolSize,
    ResponseDelivery delivery) {
    mCache = cache;
    mNetwork = network;
    mDispatchers = new NetworkDispatcher[threadPoolSize];
    mDelivery = delivery;
    }

    public CacheDispatcher(
    BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
    Cache cache, ResponseDelivery delivery) {
    mCacheQueue = cacheQueue;
    mNetworkQueue = networkQueue;
    mCache = cache;
    mDelivery = delivery;
    }

    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
    Network network, Cache cache,
    ResponseDelivery delivery) {
    mQueue = queue;
    mNetwork = network;
    mCache = cache;
    mDelivery = delivery;
    }

    如何关联的???
    Volley 类中:
    ...
    RequestQueue queue;
    ...
    queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    ...
    queue.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();
    }
    }

    mCacheQueue 为 PriorityBlockingQueue<Request<?>>
    有初始值,且在 public <T> Request<T> add(Request<T> request) {} 方法中:
    mCacheQueue.add(request);

    mNetworkQueue 为 PriorityBlockingQueue<Request<?>>
    有初始值,且在 public <T> Request<T> add(Request<T> request) {} 方法中:
    mNetworkQueue.add(request);

    mCache 为 DiskBasedCache 构造函数中赋值
    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
    Network network, Cache cache,
    ResponseDelivery delivery) {
    ...
    mCache = cache;
    ...
    }

    我们在主线程发出的请求,如StringRequest,add 到 RequestQueue 中,
    RequestQueue 会根据请求是否缓存,是否已经有该请求的缓存等来判断是添加到 NetworkQueue 还是 CacheQueue。
    NetworkDispatcher 和 CacheDispatcher 在 RequestQueue 启动时就在运行,分别从 NetworkQueue 和 CacheQueue
    中取出请求,进行处理,处理完成的结果,由 ExecutorDelivery 进行分发到主线程。

    网络的处理:
    NetworkResponse networkResponse = mNetwork.performRequest(request);
    利用 BasicNetwork 执行请求,获取网络响应。
    Response<?> response = request.parseNetworkResponse(networkResponse);
    利用对应的请求,将网络响应解析称对应的数据。如 StringRequest,则解析成 String。

    缓存的处理:
    Cache.Entry entry = mCache.get(request.getCacheKey());
    根据请求的缓存 key 值,拿出请求对应的缓存。
    Response<?> response = request.parseNetworkResponse(
    new NetworkResponse(entry.data, entry.responseHeaders));
    利用对应的请求,将网络响应解析称对应的数据。如 StringRequest,则解析成 String。
    ====================================================================================================
    DiskBasedCache 类
    实现了 Cache 接口
    Volley 只有磁盘缓存,没有内存缓存。

    public synchronized Entry get(String key) {}
    根据 key 获取 Entry
    1.获取缓存头部 CacheHeader entry = mEntries.get(key);
    2.用计数流读取缓存头部
    cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
    CacheHeader.readHeader(cis); // eat header
    3.将流(除去缓存头部后的)转换为字节数组
    byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
    return entry.toCacheEntry(data);

    public synchronized void initialize() {}
    初始化 扫描缓存目录下所有的文件 将缓存头部读取出来 存放到集合中 显然,所有的缓存头部都是在内存中
    1.File[] files = mRootDirectory.listFiles();
    2.for (File file : files) {
    3.fis = new BufferedInputStream(new FileInputStream(file));
    CacheHeader entry = CacheHeader.readHeader(fis);
    entry.size = file.length();
    putEntry(entry.key, entry);

    public synchronized void put(String key, Entry entry) {}
    将缓存 Entry 放入集合
    1.先检查缓存空间是否已满,已满则删除最近最少使用的缓存
    pruneIfNeeded(entry.data.length);
    看看这个方法 private void pruneIfNeeded(int neededSpace) {}
    遍历 Entry 集合,拿到缓存头部,再拿到文件,删除文件 计算删除后大小 如此循环 和LruCache很像
    之所以遍历,是因为 Entry 集合是 LinkedHashMap,最近使用的会放到链表的最后。
    2.拿到 Entry 对应的文件,写缓存头部,再写缓存数据。
    ====================================================================================================
    PoolingByteArrayOutputStream 类
    在进行网络请求时,将网络响应实体转为 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();
    }
    }
    相比与 ByteArrayOutputStream 的优势是:
    使用 PoolingByteArrayOutputStream 在向自身 byte[] 缓冲区写数据时,expand 获取的缓冲区是从 byte[] 池中获取
    的,heap 的性能表现会更好。
    ByteArrayOutputStream 向缓存区写数据时,会不断扩大自身缓存区,每次都是 new byte[],查看源码就知道了。
    private void expand(int i) {
    /* Can the buffer handle @i more bytes, if not expand it */
    if (count + i <= buf.length) {
    return;
    }
    byte[] newbuf = mPool.getBuf((count + i) * 2);
    System.arraycopy(buf, 0, newbuf, 0, count);
    mPool.returnBuf(buf);
    buf = newbuf;
    }

    这里同时也揭露了一个问题,Volley是将请求响应的实体放到了 byte[] 中,如果用 Volley 下载文件,问题可想而知。
    ====================================================================================================
  • 相关阅读:
    Vue-Router路由 Vue-CLI脚手架和模块化开发 之 路由常用配置与路由嵌套
    (最大上升子序列) Super Jumping! Jumping! Jumping! -- hdu -- 1087
    最大连续子序列 -- hdu -- 1231
    (KMP灵活运用 利用Next数组 )Theme Section -- hdu -- 4763
    (KMP 水)Wow! Such Doge! -- hdu -- 4847
    (回文串 Manacher )Girls' research -- hdu -- 3294
    (回文串 Manacher)吉哥系列故事——完美队形II -- hdu -- 4513
    (回文串 )Best Reward -- hdu -- 3613
    Center Alignment
    Chat Server's Outgoing Traffic
  • 原文地址:https://www.cnblogs.com/aprz512/p/5312331.html
Copyright © 2011-2022 走看看