码迷,mamicode.com
首页 > 其他好文 > 详细

Volley框架源码浅析(一)

时间:2014-05-14 23:48:28      阅读:575      评论:0      收藏:0      [点我收藏+]

标签:volley   开源项目   android开发   框架   

尊重原创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);
		//启动请求队列,其实里面就是启动了一些线程,不断监听是否有请求
        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);
    }
}


Volley类主要用来创建一个请求队列,我们的任何请求(请求字符串,json,xml)都放入到这个队列中(其实里面有两个队列,后面我们慢慢学习,暂时简单理解为一个)。创建完队列后,调用start方法,就会启动一些线程(暂时不关注多少条线程),不断监听队里里面是否有请求,如果有请求则执行http请求,在2.3之前的版本中,Http请求是通过httpClient实现,在2.3以后的版本中是通过HttpURLConnection实现,因为在2.3之前的版本中HttpRlConnection非常不稳定


(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可以缓存,那么先加入本地队列,如果不能缓存则加入网络队列



待续。。。。。。。

欢迎留言讨论。。。

Volley框架源码浅析(一),布布扣,bubuko.com

Volley框架源码浅析(一)

标签:volley   开源项目   android开发   框架   

原文地址:http://blog.csdn.net/yuanzeyao/article/details/25837897

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!