zoukankan      html  css  js  c++  java
  • Volley源码解析

    说点题外话,将近三个半月没有写博客了,年初换工作,在新的公司,上班第三天开始干活,花了二十来天弄了一个项目,上线后,接着又迭代了几个版本,不知不觉,试用期过完,才稍微有几天闲时。在年初的时候,就一直在想,
    将圈内的几个流行的网络框架的源码分析分析,但是又但是水平不够,有些分析的不好,那就尴尬了....所以花了点时间好好看了一下,走了一遍这些源码,决定试一试,相当于做个笔记吧。今天就从一个相对轻量级的网络请求框架下手--Volley。

    Volley
    提起这个Volley,很多同学应该都很熟悉,但是我面试过蛮多人,问起,对volley的了解,基本上就说,里面对图片做了缓存,在2.3之前用的是HTTPClient,
    2.3后用的是HttpURLConnection,然后就没了.... 虽然这没什么错,但是对于一个有经验的开发人员来说,这样的认识,太过于表面了。我们知道Volley是
    在2013年Google I/O大会上推出了一个新的网络通信框架,他的设计目的就是为了那些请求数据量不是特别大,但是又是特别频繁的网络操作非常适合。但是对于
    数据量较大的请求,比如说下载一个较大的文件,Volley可能相比于其他的框架,就有点不足了....

    Volley简单使用
    我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖。

    好了,接下来上代码了.....

     1         //获取volley的请求对象
     2         RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
     3         StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
     4             @Override
     5             public void onResponse(String s) {
     6                 Log.d("MainActivity", "----->" + s);
     7 
     8             }
     9         }, new Response.ErrorListener() {
    10             @Override
    11             public void onErrorResponse(VolleyError volleyError) {
    12                 Log.d("MainActivity", "---volleyError-->" + volleyError);
    13             }
    14         });
    15         requestQueue.add(stringRequest);

    从代码可以看出,首先newRequestQueue来获取到一个请求队列,然后在将StringRequest这个请求添加到请求队列中,就可以了,就是这么简单。当然请求不值
    StringRequest,还有JsonObjectRequest ,ImageRequest等等但是用法都是一样的,这里就不贴代码了。接着...就没了,Volley的简单使用就这样可以进行请
    求了。是不是很简单....

    但是这个不是本篇的重点,重点是分析一下这些是怎么执行的。先上一张图,这张图是在网上拿过来用的...

    我们先看看newRequestQueue这个内部是怎么执行的,代码一开始连续执行了几个重载方法,最后走到newRequestQueue 

     1 public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
     2         File cacheDir = new File(context.getCacheDir(), "volley");
     3         String userAgent = "volley/0";
     4 
     5         try {
     6             String packageName = context.getPackageName();
     7             PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
     8             userAgent = packageName + "/" + info.versionCode;
     9         } catch (NameNotFoundException var7) {
    10             ;
    11         }
    12 
    13         //这里进行了一个版本的判断 2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
    14         if (stack == null) {
    15             if (VERSION.SDK_INT >= 9) {
    16                 stack = new HurlStack();
    17             } else {
    18                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
    19             }
    20         }
    21 
    22         Network network = new BasicNetwork((HttpStack)stack);
    23         RequestQueue queue;
    24         if (maxDiskCacheBytes <= -1) {
    25             queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    26         } else {
    27             queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    28         }
    29 
    30         queue.start();
    31         return queue;
    32     }

    在这里,我们看到了一个版本判断,是不是瞬间感觉有点熟悉,没错,我们前面说的,volley2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
    就是在这里进行判断的。接着看queue.start();

     1 public void start() {
     2         this.stop();
     3         this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
     4         this.mCacheDispatcher.start();
     5 
     6         for(int i = 0; i < this.mDispatchers.length; ++i) {
     7             NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
     8             this.mDispatchers[i] = networkDispatcher;
     9             networkDispatcher.start();
    10         }
    11 
    12     }

    mCacheDispatcher是缓存调度线程,NetworkDispatcher是网络调度线程,而这个this.mDispatchers.length系统默认的大小为4,也就是说,在这里总共启动了5个线程在后台运行。
    好了,到这里,就可以了,看源码不要每一行都弄懂,不然,出不来了。到这里就拿到了这个RequestQueue对象。回过头来看前面使用的代码

     1         //获取volley的请求对象
     2         RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
     3         StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
     4             @Override
     5             public void onResponse(String s) {
     6                 Log.d("MainActivity", "----->" + s);
     7 
     8             }
     9         }, new Response.ErrorListener() {
    10             @Override
    11             public void onErrorResponse(VolleyError volleyError) {
    12                 Log.d("MainActivity", "---volleyError-->" + volleyError);
    13             }
    14         });
    15         requestQueue.add(stringRequest);

    我们拿到这个RequestQueue对象以后,然后就把这个请求通过add方法添加到队列中,我们看看这个add()方法是怎么执行的。

     1 public <T> Request<T> add(Request<T> request) {
     2         request.setRequestQueue(this);
     3         Set var2 = this.mCurrentRequests;
     4         synchronized(this.mCurrentRequests) {
     5             this.mCurrentRequests.add(request);
     6         }
     7 
     8         request.setSequence(this.getSequenceNumber());
     9         request.addMarker("add-to-queue");
    10         if (!request.shouldCache()) { //如果不能缓存
    11             this.mNetworkQueue.add(request);
    12             return request;
    13         } else {
    14             Map var7 = this.mWaitingRequests;
    15             synchronized(this.mWaitingRequests) {
    16                 String cacheKey = request.getCacheKey();
    17                 if (this.mWaitingRequests.containsKey(cacheKey)) { //判断之前是否执行过,但是还没有返回结果
    18                     Queue<Request<?>> stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
    19                     if (stagedRequests == null) {
    20                         stagedRequests = new LinkedList();
    21                     }
    22 
    23                     ((Queue)stagedRequests).add(request);
    24                     this.mWaitingRequests.put(cacheKey, stagedRequests);
    25                     if (VolleyLog.DEBUG) {
    26                         VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
    27                     }
    28                 } else {
    29                 //没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据
    30                     this.mWaitingRequests.put(cacheKey, (Object)null);
    31                     this.mCacheQueue.add(request);
    32                 }
    33 
    34                 return request;
    35             }
    36         }
    37     }

    从代码中可以看出,首先判断是否可以缓存,当然,默认是可以缓存的。如果不能缓存的话,则通过this.mNetworkQueue.add(request);将请求添加到网络请求队列中。如果可以缓存
    则还会判断一次这个请求是否请求,如果执行过就就通过this.mWaitingRequests.put(cacheKey, stagedRequests);添加到mWaitingRequests队列,不在重复请求。否则就加入到缓存队列。
    大体的流程是这样。现在我们看看缓存的,和网络的是怎么执行的。我们找到start()方法

     1 public void start() {
     2         this.stop();
     3         this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
     4         this.mCacheDispatcher.start();
     5 
     6         for(int i = 0; i < this.mDispatchers.length; ++i) {
     7             NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
     8             this.mDispatchers[i] = networkDispatcher;
     9             networkDispatcher.start();
    10         }
    11 
    12     }

    先看CacheDispatcher,找到run()方法

     1 public void run() {
     2         if (DEBUG) {
     3             VolleyLog.v("start new dispatcher", new Object[0]);
     4         }
     5 
     6         Process.setThreadPriority(10);
     7         this.mCache.initialize();
     8 
     9         while(true) {
    10             while(true) {
    11                 while(true) {
    12                     while(true) {
    13                         try {
    14                             while(true) {
    15                                 final Request<?> request = (Request)this.mCacheQueue.take(); //从缓存队列中获取到一个请求
    16                                 request.addMarker("cache-queue-take");
    17                                 if (request.isCanceled()) { //判断请求是否取消,如果取消了,那就将该请求finish掉
    18                                     request.finish("cache-discard-canceled");
    19                                 } else {
    20                                     Entry entry = this.mCache.get(request.getCacheKey());
    21                                     if (entry == null) {//如果从缓存中取出来的内容为空,则将请求加入到网络线程中再次请求
    22                                         request.addMarker("cache-miss");
    23                                         this.mNetworkQueue.put(request);
    24                                     } else if (entry.isExpired()) { //如果请求过期了,则将请求加入到网络线程中再次请求
    25                                         request.addMarker("cache-hit-expired");
    26                                         request.setCacheEntry(entry);
    27                                         this.mNetworkQueue.put(request);
    28                                     } else { //将数据回调到主线程
    29                                         request.addMarker("cache-hit");
    30                                         Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
    31                                         request.addMarker("cache-hit-parsed");
    32                                         if (entry.refreshNeeded()) {
    33                                             request.addMarker("cache-hit-refresh-needed");
    34                                             request.setCacheEntry(entry);
    35                                             response.intermediate = true;
    36                                             this.mDelivery.postResponse(request, response, new Runnable() {
    37                                                 public void run() {
    38                                                     try {
    39                                                         CacheDispatcher.this.mNetworkQueue.put(request);
    40                                                     } catch (InterruptedException var2) {
    41                                                         ;
    42                                                     }
    43 
    44                                                 }
    45                                             });
    46                                         } else {
    47                                             this.mDelivery.postResponse(request, response);
    48                                         }
    49                                     }
    50                                 }
    51                             }
    52                         } catch (InterruptedException var4) {
    53                             if (this.mQuit) {
    54                                 return;
    55                             }
    56                         }
    57                     }
    58                 }
    59             }
    60         }
    61     }

    这里嵌套了几个循环,有点凌乱啊,但是慢慢分析的话,就会发现,其实很清晰。我在注释上面写了,这里就不重复了

    我们在看看NetworkDispatcher,看看网络线程是怎么执行的。一样找到run()方法

     1 public void run() {
     2         Process.setThreadPriority(10);
     3 
     4         while(true) {
     5             long startTimeMs;
     6             Request request;
     7             while(true) {
     8                 startTimeMs = SystemClock.elapsedRealtime();
     9 
    10                 try {
    11                     request = (Request)this.mQueue.take(); //获取到一个请求
    12                     break;
    13                 } catch (InterruptedException var6) {
    14                     if (this.mQuit) {
    15                         return;
    16                     }
    17                 }
    18             }
    19 
    20             try {
    21                 request.addMarker("network-queue-take");
    22                 if (request.isCanceled()) { //如果请求取消了,则将请求finish掉
    23                     request.finish("network-discard-cancelled");
    24                 } else {//进行网络请求
    25                     this.addTrafficStatsTag(request);
    26                     NetworkResponse networkResponse = this.mNetwork.performRequest(request);
    27                     request.addMarker("network-http-complete");
    28                     if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    29                         request.finish("not-modified");
    30                     } else {
    31                         Response<?> response = request.parseNetworkResponse(networkResponse);
    32                         request.addMarker("network-parse-complete");
    33                         if (request.shouldCache() && response.cacheEntry != null) {
    34                             this.mCache.put(request.getCacheKey(), response.cacheEntry);
    35                             request.addMarker("network-cache-written");
    36                         }
    37 
    38                         request.markDelivered();
    39                         this.mDelivery.postResponse(request, response);
    40                     }
    41                 }
    42             } catch (VolleyError var7) {
    43                 var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    44                 this.parseAndDeliverNetworkError(request, var7);
    45             } catch (Exception var8) {
    46                 VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
    47                 VolleyError volleyError = new VolleyError(var8);
    48                 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    49                 this.mDelivery.postError(request, volleyError);
    50             }
    51         }
    52     }

    代码比较多,我们直接找到NetworkResponse networkResponse = this.mNetwork.performRequest(request);这句代码,这句代码就是请求网络的代码,最核心的。performRequest是一个接口,我们看看这个
    performRequest()方法。Network在最开始说版本判断的时候里面有一句代码Network network = new BasicNetwork((HttpStack)stack); 从这句代码,我们可以知道BasicNetwork才是最终
    实现网络请求的类,我们找到performRequest方法

     1 public NetworkResponse performRequest(Request<?> request) throws VolleyError {
     2         long requestStart = SystemClock.elapsedRealtime();
     3 
     4         while(true) {
     5             HttpResponse httpResponse = null;
     6             byte[] responseContents = null;
     7             Map responseHeaders = Collections.emptyMap();
     8 
     9             try {
    10                 Map<String, String> headers = new HashMap();
    11                 this.addCacheHeaders(headers, request.getCacheEntry());
    12                 httpResponse = this.mHttpStack.performRequest(request, headers);
    13                 StatusLine statusLine = httpResponse.getStatusLine();
    14                 int statusCode = statusLine.getStatusCode();
    15                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());
    16                 if (statusCode == 304) {
    17                     Entry entry = request.getCacheEntry();
    18                     if (entry == null) {
    19                         return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
    20                     }
    21 
    22                     entry.responseHeaders.putAll(responseHeaders);
    23                     return new NetworkResponse(304, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
    24                 }
    25 
    26                 if (statusCode == 301 || statusCode == 302) {
    27                     String newUrl = (String)responseHeaders.get("Location");
    28                     request.setRedirectUrl(newUrl);
    29                 }
    30 
    31                 byte[] responseContents;
    32                 if (httpResponse.getEntity() != null) {
    33                     responseContents = this.entityToBytes(httpResponse.getEntity());
    34                 } else {
    35                     responseContents = new byte[0];
    36                 }
    37 
    38                 long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
    39                 this.logSlowRequests(requestLifetime, request, responseContents, statusLine);
    40                 if (statusCode >= 200 && statusCode <= 299) {
    41                     return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
    42                 }
    43 
    44                 throw new IOException();
    45             } catch (SocketTimeoutException var12) {
    46                 attemptRetryOnException("socket", request, new TimeoutError());
    47             } catch (ConnectTimeoutException var13) {
    48                 attemptRetryOnException("connection", request, new TimeoutError());
    49             } catch (MalformedURLException var14) {
    50                 throw new RuntimeException("Bad URL " + request.getUrl(), var14);
    51             } catch (IOException var15) {
    52                 int statusCode = false;
    53                 NetworkResponse networkResponse = null;
    54                 if (httpResponse == null) {
    55                     throw new NoConnectionError(var15);
    56                 }
    57 
    58                 int statusCode = httpResponse.getStatusLine().getStatusCode();
    59                 if (statusCode != 301 && statusCode != 302) {
    60                     VolleyLog.e("Unexpected response code %d for %s", new Object[]{statusCode, request.getUrl()});
    61                 } else {
    62                     VolleyLog.e("Request at %s has been redirected to %s", new Object[]{request.getOriginUrl(), request.getUrl()});
    63                 }
    64 
    65                 if (responseContents == null) {
    66                     throw new NetworkError(networkResponse);
    67                 }
    68 
    69                 networkResponse = new NetworkResponse(statusCode, (byte[])responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
    70                 if (statusCode != 401 && statusCode != 403) {
    71                     if (statusCode != 301 && statusCode != 302) {
    72                         throw new ServerError(networkResponse);
    73                     }
    74 
    75                     attemptRetryOnException("redirect", request, new AuthFailureError(networkResponse));
    76                 } else {
    77                     attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
    78                 }
    79             }
    80         }
    81     }

    代码比较多,但是大多数代码是判断状态返回码的,不需要理会。我们直接看httpResponse = this.mHttpStack.performRequest(request, headers);这一句代码,HttpStack这个有没有很熟悉。没有??没关系我在复制一次代码

    1 if (stack == null) {
    2             if (VERSION.SDK_INT >= 9) {
    3                 stack = new HurlStack();
    4             } else {
    5                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
    6             }
    7         }

    还是在这个版本判断这里,这里就是HurlStack就是真正的网络请求的类了,网络请求,就是写在这个类里面的。好了,volley整个流程大概就是这样了。现在大家回过头看最初的哪一张图,是不是明了很多。

  • 相关阅读:
    docker学习之路-nginx镜像(翻译)
    docker学习之路-centos下安装docker
    Angular复习笔记7-路由(下)
    Linux保证运行一个实例
    使用epoll实现简单的服务器
    vmware中centos、redhat桥接网络配置
    同步队列、线程池模式服务器消息处理类
    ocilib linux编译安装
    redhat6.5安装oracle11_2R
    redhat6.5安装yum
  • 原文地址:https://www.cnblogs.com/huangjialin/p/9337801.html
Copyright © 2011-2022 走看看