本文主要是介绍[置顶] Android网络通信Volley框架源码浅析(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
[置顶] Android网络通信Volley框架源码浅析(一)
尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897
从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Volley框架意外,也有一些http请求开源项目,比如使用比较广泛的有async-http,UniversImageLoader等等,其中async-http主要用来实现异步http请求,而后者主要用来请求图片。Volley具有以上两种框架的功能,并且是Google公司发布,我想作为Android开发者,很有必要研究一下该框架。
1、下载Volley框架
git clone https://android.googlesource.com/platform/frameworks/volley
2、引用Volley框架
引用该框架的方式主要有两种:
(1):直接导入Volley框架,作为lib使用
(2):编译Volley成jar包
3、Volley的功能:
前面已经提及了Volley是一个用于http请求的框架,其主要功能如下:
json,xml,String,Image等资源的请求,当然我们还可以根据自己的需要来改写Volley框架源码,从而实现自己的功能
4、Volley源码分析
温馨提醒:如果是第一次看Volley源码,第一遍没看懂没关系,将源码copy下来,跟着我的思路慢慢分析,将文章从头到后多看几遍就ok了,因为Volley的一些关键类都互相应用,我只能一个一个的分析了,等你看完我所有的文章,然后再从头看一变,相信你定有所收获
当然阅读次文章之前,最好学会知道Volley框架的基本使用,由于网络上很多类似的教程,我在此处就不再描述了,后期如果有时间我也会讲解一下Volley的应用
我们就从Volley这个类开始吧
(1) Volley.java
- public class Volley {
- //缓存目录
- private static final String DEFAULT_CACHE_DIR = "volley";
- //创建一个默认的请求队列,我们的请求创建好后,放入该队列即可
- public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
- //缓存目录
- File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
- 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) {
- /**
- 如果我们没有传入stack,那么自己创建一个,如果sdk>9(就是2.3以上),那么使用
- HttpURLConnection实现http请求,如果2.3以前使用HttpClient实现,因为在2.3以前httpURLConnection不稳定
- */
- 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类是一个网络请求类,使用stack进行网络请求
- Network network = new BasicNetwork(stack);
- //真正创建一个请求队列,传入一个磁盘缓存和网络请求类
- RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
- //启动请求队列,其实里%9��就是启动了一些线程,不断监听是否有请求
- queue.start();
- return queue;
- }
- /**
- * 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.
- * @return A started {@link RequestQueue} instance.
- */
- public static RequestQueue newRequestQueue(Context context) {
- return newRequestQueue(context, null);
- }
- }
(2) HttpStack.java
下面看看HttpStack是何方神圣
- public interface HttpStack {
- /**
- 名字挺吓人的,呵呵,其实就是一个接口,它有两个实现,分别是HurlStack,HttpClientStack,通过名字大家
- 可以猜出来一个基于HttpClient,一个基于HttpURLConnection
- * @return the HTTP response
- */
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError;
- }
直接查看它的子类方法吧
首先看 HurlStack.java类
这个类是基于HttpURLConnection实现的
由于这个类比较长,我就重点讲解一下
(3) HurlStack.java
- /**
- 继承自HttpStack,我们暂时就把Request抽象成一个请求,包括url,method等信息,后期我们会重点分析这个类
- 第二个参数就是一些请求头信息
- */
- @Override
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError {
- String url = request.getUrl();
- HashMap<String, String> map = new HashMap<String, 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));
- }
- //这个方法名字很长,其实功能很简单,就是为connection设置请求方法 如get post等等
- 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<String, List<String>> header : connection.getHeaderFields().entrySet()) {
- if (header.getKey() != null) {
- Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
- response.addHeader(h);
- }
- }
- //http的返回结果
- return response;
- }
(4) HttpClientStack.java
- /**
- 相对比上一个方法简单,相信使用过httpClient的同学一看就明白
- */
- @Override
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError {
- //这个方法见名知意,就是创建一个HttpGet或者HttpPost
- HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
- //添加头信息
- addHeaders(httpRequest, additionalHeaders);
- addHeaders(httpRequest, request.getHeaders());
- //在请求之前进行准备工作,其实是个空方法,很想AsyncTask的onPreExecute
- onPrepareRequest(httpRequest);
- HttpParams httpParams = httpRequest.getParams();
- int timeoutMs = request.getTimeoutMs();
- // TODO: Reevaluate this connection timeout based on more wide-scale
- // data collection and possibly different for wifi vs. 3G.
- HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
- HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
- //执行请求并返回结果
- return mClient.execute(httpRequest);
- }
看到这里大家肯定觉得这个框架也没有什么了不起嘛,和使用HttpURLConnection和HttpClient差不多嘛,如果你真的这样觉得那么你就大错特错了,其实这个框架的核心在于线程的调度和缓存上面,后期我们会介绍的
回到Volley类,我们看下一个陌生的类就是Network,其实Network不过是个接口而已,它的实现类是BaskNetwork
(5) BaskicNetwork.java
从名字我们就可以看出来,这个类就是进行网络请求的,其实他就是对HttpurlStack或者HttpClientStack的一个封装,真正实现请求的还是上面两个类。
最核心的方法:
- @Override
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- long requestStart = SystemClock.elapsedRealtime();
- while (true) {
- HttpResponse httpResponse = null;
- byte[] responseContents = null;
- Map<String, String> responseHeaders = new HashMap<String, String>();
- try {
- // Gather headers.
- Map<String, String> headers = new HashMap<String, String>();
- addCacheHeaders(headers, request.getCacheEntry());
- //调用mHttpStack执行http请求
- 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) {
- //将返回结果封装成一个NetworkResponse
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
- request.getCacheEntry() == null ? null : 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());
- } catch (ConnectTimeoutException e) {
- //超时重新请求
- attemptRetryOnException("connection", request, new TimeoutError());
- } 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);
- }
- }
- }
- }
这个方法调用了mHttpStack的同名方法,只不过在mHttpStack中返回的是HttpResponse,在这里返回的是NetworkResponse。
然后再看看本篇文章的最后一个类:
RequestQueue.java
我保留了一些关键字段,删除不影响理解的字段
- public class RequestQueue {
- ...
- //本地缓存队列,如果一个请求能够缓存,那么先放到这个队列中,如果本地缓存没有命中,则加入网络队列,见后面
- private final PriorityBlockingQueue<Request<?>> mCacheQueue =
- new PriorityBlockingQueue<Request<?>>();
- //网络请求队列
- private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
- new PriorityBlockingQueue<Request<?>>();
- //默认的网络请求线程个数 默认四个,这个我们可以改动
- private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
- //本地缓存的接口
- private final Cache mCache;
- //这个类相信大家并不陌生
- private final Network mNetwork;
- //由于网络请求在子线程中执行,这个对象将请求结果发送到ui线程,功能很像Handler
- private final ResponseDelivery mDelivery;
- //网络线程数组
- private NetworkDispatcher[] mDispatchers;
- //本地线程数组 只有一条
- private CacheDispatcher mCacheDispatcher;
- /**
- 创建一个请求队列
- 参数1:本地缓存
- 参数2: network 进行网络进行的包装类
- 参数3:网络请求线程池大小
- 参数4:就是一个将子线程的数据发送到ui线程的功能类,先可以不用关心
- */
- public RequestQueue(Cache cache, Network network, int threadPoolSize,
- ResponseDelivery delivery) {
- mCache = cache;
- mNetwork = network;
- mDispatchers = new NetworkDispatcher[threadPoolSize];
- mDelivery = delivery;
- }
- public RequestQueue(Cache cache, Network network, int threadPoolSize) {
- this(cache, network, threadPoolSize,
- new ExecutorDelivery(new Handler(Looper.getMainLooper())));
- }
- /**
- * Creates the worker pool. Processing will not begin until {@link #start()} is called.
- *
- * @param cache A Cache to use for persisting responses to disk
- * @param network A Network interface for performing HTTP requests
- */
- public RequestQueue(Cache cache, Network network) {
- this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
- }
- //启动本地和网络线程
- 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();
- }
- }
- //关闭本地和网络线程
- public void stop() {
- if (mCacheDispatcher != null) {
- mCacheDispatcher.quit();
- }
- for (int i = 0; i < mDispatchers.length; i++) {
- if (mDispatchers[i] != null) {
- mDispatchers[i].quit();
- }
- }
- }
- //相当于一个过滤器,对于apply方法返回true的Request可以从Queue中删除
- public interface RequestFilter {
- public boolean apply(Request<?> request);
- }
- //借助上面的方法实现对没有执行的Request进行删除
- public void cancelAll(RequestFilter filter) {
- synchronized (mCurrentRequests) {
- for (Request<?> request : mCurrentRequests) {
- if (filter.apply(request)) {
- request.cancel();
- }
- }
- }
- }
- //取消所有的请求
- 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;
- }
- });
- }
- /**
- 将Request放入两个队列中的一个
- */
- 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");
- // 如果该Request不能缓存,那么直接放入网络队列
- if (!request.shouldCache()) {
- mNetworkQueue.add(request);
- return request;
- }
- // 把Request放入具有相同CacheKey的链表中,如果没有相同的CacheKey的Request请求存在,则放入本地队列
- synchronized (mWaitingRequests) {
- 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;
- }
- }
- /**
- * 结束一个Request
- */
- void finish(Request<?> request) {
- // Remove from the set of requests currently being processed.
- synchronized (mCurrentRequests) {
- mCurrentRequests.remove(request);
- }
- if (request.shouldCache()) {
- synchronized (mWaitingRequests) {
- String cacheKey = request.getCacheKey();
- Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
- if (waitingRequests != null) {
- if (VolleyLog.DEBUG) {
- VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
- waitingRequests.size(), cacheKey);
- }
- // Process all queued up requests. They won't be considered as in flight, but
- // that's not a problem as the cache has been primed by 'request'.
- mCacheQueue.addAll(waitingRequests);
- }
- }
- }
- }
- }
写到这里先高一段落吧,来个小小的总结:Volley中有一个RequestQueue(包含本地队列和网络队列),就是请求队列,每一个http请求都被封装成了一个Request,通过队列的add方法加入队列,如果一个Request可以缓存,那么先加入本地队列,如果不能缓存则加入网络队列
这篇关于[置顶] Android网络通信Volley框架源码浅析(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!