(干货) Android Volley框架源码详细解析

前言

  经常接触Android网络编程的我们,对于Volley肯定不陌生,但我们不禁要问,对于Volley我们真的很了解吗?Volley的内部是怎样实现的?为什么几行代码就能快速搭建好一个网络请求?我们不但要知其然,也要知其所以然,抱着这样的目的,本文主要详细讲述Volley的源码,对内部流程进行详细解析。

Part 1.从RequestQueue说起

  (1)还记得搭建请求的第一步是什么吗?是新建一个请求队列,比如说这样:

RequestQueue queue = Volley.newRequestQueue(context)

  虽然表面上只是一句代码的事情,但是背后做了很多准备工作,我们追踪源码,找到Volley#newRequestQueue()方法:

 /**
   * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
   * You may set a maximum size of the disk cache in bytes.
   *
   * @param context A {@link Context} to use for creating the cache dir.
   * @param stack An {@link HttpStack} to use for the network, or null for default.
   * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.
   * @return A started {@link RequestQueue} instance.
   */
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
    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) {
    }

     /**
       * 根据不同的系统版本号实例化不同的请求类,如果版本号小于9,用HttpClient
       * 如果版本号大于9,用HttpUrlConnection
       */
    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));
        }
    }
      //把实例化的stack传递进BasicNetwork,实例化Network
    Network network = new BasicNetwork(stack);
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1){
       // No maximum size specified
       //实例化RequestQueue类
       queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else {
       // Disk cache size specified
       queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }
     //调用RequestQueue的start()方法
    queue.start();
    return queue;
}

  首先我们看参数,有三个,实际上我们默认使用了只有一个参数context的方法,这个是对应的重载方法,最终调用的是三个参数的方法,context是上下文环境;stack代表需要使用的网络连接请求类,这个一般不用设置,方法内部会根据当前系统的版本号调用不同的网络连接请求类(HttpUrlConnection和HttpClient);最后一个参数是缓存的大小。接着我们看方法内部,这里先创建了缓存文件,然后根据不同的系统版本号实例化不同的请求类,用stack引用这个类。接着又实例化了一个BasicNetwork,这个类在下面会说到。然后到了实际实例化请求队列的地方:new RequestQueue(),这里接收两个参数,分别是缓存和network(BasicNetwork)。实例化RequestQueue后,调用了start()方法,最后返回这个RequestQueue。

  (2)我们跟着RequestQueue看看它的构造器做了哪些工作:

/**
     * 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
     * @param threadPoolSize Number of network dispatcher threads to create
     * @param delivery A ResponseDelivery interface for posting responses and errors
     */
    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        //实例化网络请求数组,数组大小默认是4
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                //ResponseDelivery是一个接口,实现类是ExecutorDelivery
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

    public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }

  可以看到,把传递过来的cache和network作为变量传递给了四个参数的构造器,在这里,初始化了RequestQueue的几个成员变量:mCache(文件缓存)、mNetwork(BasicNetwork实例)、mDispatchers(网络请求线程数组)、以及mDelivery(派发请求结果的接口),具体意义可看上面的注解。

  (3)构造完RequestQueue后,从(1)可知,最后调用了它的start()方法,我们来看看这个方法,RequestQueue#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();
        }
    }

  首先实例化了CacheDispatcher,CacheDispatcher类继承自Thread,接着调用了它的start()方法,开始了一条新的缓存线程。接着是一个for循环,根据设置的mDispatchers数组大小来开启多个网络请求线程,默认是4条网络请求线程。

  到目前为止,Volley.newRequestQueue()方法完成了,即我们的网络请求第一步,建立请求队列完成。

  先小结一下:建立请求队列所做的工作是,创建文件缓存(默认),实例化BasicNetwork,实例化Delivery用于发送线程请求,创建一条缓存线程和四条网络请求线程(默认)并运行。

Part 2.网络请求的实现原理

  在创建完请求队列后,接着就是建立一个请求,请求的方式可以是StringRequest、JsonArrayRequest或者ImageRequest等,那么这些请求的背后原理是什么呢?我们拿最简单的StringRequest来说,它继承自Request,而Request则是所有请求的父类,所以说如果你要自定义一个网络请求,就应该继承自Request。接下来我们看看StringRequest的源码,因为不管Request的子类是什么,大体的实现思路都是一致的,所以我们弄懂了StringRequest,那么对于其他的请求类的理解是相通的。如下是StringRequest源码:

public class StringRequest extends Request<String> {
    private Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    ...
    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}

  源码并不长,我们主要关注的是deliverResponse方法和parseNetworkResponse。可以看出,这两个方法都是重写的,我们翻看父类Request的对应方法,发现是抽象方法,说明这两个方法在每一个自定义的Request中都必须重写。这里简单说说这两个方法的作用。先看deliverResponse方法:它内部调用了mListener.onResponse(response)方法,而这个方法正是我们在写一个请求的时候,添加的listener所重写的onResponse方法,也就是说,响应成功后在这里调用了onResponse()方法。接着看pareNetworkResponse方法,可以看出这里主要是对response响应做出一些处理。可以对比一下不同请求类的这个方法,都会不同的,所以说,这个方法是针对不同的请求类型而对响应做出不同的处理。比如说,如果是StringRequest,则将响应包装成String类型;如果是JsonObjectRequest,则将响应包装成JsonObject。那么现在应该清楚了:对于想要得到某一种特殊类型的请求,我们可以自定义一个Request,重写这两个方法即可。

  这里**小结一下:**Request类做的工作主要是初始化一些参数,比如说请求类型、请求的url、错误的回调方法;而它的任一子类重写deliverResponse方法来实现成功的回调,重写parseNetworkResponse()方法来处理响应数据;至此,一个完整的Request请求搭建完毕。

Part 3.添加请求

  前面已经完成了请求队列的创建,Request请求的创建,那么接下来就是把请求添加进队列了。我们看RequestQueue#add()源码

/**
 * Adds a Request to the dispatch queue.
 * @param request The request to service
 * @return The passed-in 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.
    //标记当前请求,表示这个请求由当前RequestQueue处理
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    // Process requests in the order they are added.
    //获得当前请求的序号
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");
    // If the request is uncacheable, skip the cache queue and go straight to the network.
    //如果请求不能缓存,直接添加到网络请求队列,默认是可以缓存
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }

    // Insert request into stage if there‘s already a request with the same cache key in flight.
    // 锁定当前代码块,只能一条线程执行
    synchronized (mWaitingRequests) {
        String cacheKey = request.getCacheKey();

        //是否有相同请求正在处理
        if (mWaitingRequests.containsKey(cacheKey)) {
            // There is already a request in flight. Queue up.
            //如果有相同请求正在处理,那么把这个请求放进mWaitingRequest中,等待。
            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中,同时也放进mCacheQueue缓存队列中
            //这代表这个请求已经开始在缓存线程中运行了
            mWaitingRequests.put(cacheKey, null);
            mCacheQueue.add(request);
        }
        return request;
    }
}

  结合相应的注释,我们得出如下结论:在这个add方法中,主要判断一个Request请求是否可以缓存(默认是可以缓存的),如果不可以则直接添加到网络请求队列开始网络通信;如果可以,则进一步判断当前是否有相同的请求正在进行,如果有相同的请求,则让这个请求排队等待,如果没有相同的请求,则直接放进缓存队列中。如果对此还有什么疑问,可以看下面的流程图(图片来自网络):

Part 4.缓存线程

  在part1的最后实例化了缓存线程并开始运行,一直处于等待状态,而上面把请求添加进了缓存线程,此时缓存线程就开始真正的工作了。我们来看缓存线程的源码,主要看它的run()方法,CacheDispatcher#run():

@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    // Make a blocking call to initialize the cache.
    mCache.initialize();

    Request<?> request;
    while (true) {
        // release previous request object to avoid leaking request object when mQueue is drained.
        request = null;
        try {
            // Take a request from the queue.
            //从缓存队列中取出请求
            request = mCacheQueue.take();
        } ...
        try {
            request.addMarker("cache-queue-take");

            // If the request has been canceled, don‘t bother dispatching it.
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // Attempt to retrieve this item from cache.
            //从文件缓存中取出这个请求的结果
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                // Cache miss; send off to the network dispatcher.
                mNetworkQueue.put(request);
                continue;
            }

            // If it is completely expired, just send it to the network.
            //判断缓存是否过期
            if (entry.isExpired()) {
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                mNetworkQueue.put(request);
                continue;
            }

            // We have a cache hit; parse its data for delivery back to the request.
            request.addMarker("cache-hit");
            //先将响应的结果包装成NetworkResponse,然后调用Request子类的
            //parseNetworkResponse方法解析数据
            Response<?> response = request.parseNetworkResponse(
                    new NetworkResponse(entry.data, entry.responseHeaders));
            request.addMarker("cache-hit-parsed");

            if (!entry.refreshNeeded()) {
                // Completely unexpired cache hit. Just deliver the response.
                //调用ExecutorDelivey#postResponse方法
                mDelivery.postResponse(request, response);
            } else {
                ....
            }
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
        }
    }
}

  在run()方法中,我们可以看到最开始有一个while(true)循环,表示它一直在等待缓存队列的新请求的出现。接着,先判断这个请求是否有对应的缓存结果,如果没有则直接添加到网络请求队列;接着再判断这个缓存结果是否过期了,如果过期则同样地添加到网络请求队列;接下来便是对缓存结果的处理了,我们可以看到,先是把缓存结果包装成NetworkResponse类,然后调用了Request的parseNetworkResponse,这个方法我们在part2说过,子类需要重写这个方法来处理响应数据。最后,把处理好的数据post到主线程,这里用到了ExecutorDelivery#postResponse()方法,下面会分析到。

  小结:CacheDispatcher线程主要对请求进行判断,是否已经有缓存,是否已经过期,根据需要放进网络请求队列。同时对相应结果进行包装、处理,然后交由ExecutorDelivery处理。这里以一张流程图显示它的完整工作流程:

Part 5.网络请求线程

  上面提到,请求不能缓存、缓存结果不存在、缓存过期的时候会把请求添加进请求队列,此时一直等待的网络请求线程由于获取到请求,终于要开始工作了,我们来看NetworkDispatcher#run()方法

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } ...

            try {
                ...
                // Perform the network request.
                //调用BasicNetwork实现类进行网络请求,并获得响应 1
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                ...

                // Parse the response here on the worker thread.
                //对响应进行解析
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");
                ...
                request.markDelivered();
                mDelivery.postResponse(request, response);

                // Write to cache if applicable.
                //将响应结果写进缓存
                if (request.shouldCache() && response.cacheEntry != null) {
                   mCache.put(request.getCacheKey(), response.cacheEntry);
                   request.addMarker("network-cache-written");
                }
            } ...
        }
    }

  源码做了适当的删减,大体上和CacheDispatcher的逻辑相同,这里关注①号代码,这里调用了BasicNetwork#perfromRequest()方法,把请求传递进去,可以猜测,这个方法内部实现了网络请求的相关操作,那么我们进去看看,BasicNetwork#perfromRequest()

@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());

                httpResponse = mHttpStack.performRequest(request, headers);  // 1
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);  // 2
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }
                ...
        }
    }

  我们主要看①号代码,mHttpStack.performRequest();这里调用了mHttpStack的performRequest方法,那么mHttpStack是什么呢?我们可以翻上去看看part1处实例化BasicNetwork的时候传递的stack值,该值就是根据不同的系统版本号而实例化的HttpStack对象(版本号大于9的是HurlStack,小于9的是HttpClientStack),由此可知,这里实际调用的是HurlStack.performRequest()方法,方法的内部基本是关于HttpUrlConnection的逻辑代码,这里就不展开说了。可以这么说:HurlStack封装好了HttpUrlConnection,而HttpClientStack封装了HttpClient。该方法返回了httpResponse,接着把这个响应交由②处处理,封装成NetworkResponse对象并返回。在NetworkDispatcher#run()方法获取返回的NetworkResponse对象后,对响应解析,通过ExecutorDelivery#postResponse()方法回调解析出来的数据,这个过程和CacheDispatcher相同。

Part 6.ExecutorDelivery 通知主线程

  在CacheDispatcher和NetworkDispatcher中最后都有调用到ExecutorDelivery#postResponse()方法,那么这个方法到底是做什么呢?由于缓存线程和网络请求线程都不是主线程,所以主线程需要有“人”通知它网络请求已经完成了,而这个“人”正是由ExecutorDelivery充当。在完成请求后,通过ExecutorDelivery#postResponse()方法,最终会回调到主线程中重写的onResponse()方法。我们看看这个方法的源码ExecutorDelivery#postResponse()

    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

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

  在方法内部调用了mResponsePoster#execute()方法,那么,这个mResponsePoster是在哪里来的呢?其实这个成员变量是在ExecutorDelivery实例化的时候同时实例化的,而ExecutorDelivery则是在RequestQueue实例化的时候同时实例化的,读者可以自行查看相应的构造方法,其实这些工作在par 1建立请求队列的时候已经全部做好了。接着我们可以看以下代码,ExecutorDelivery#ExecutorDelivery():

public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        //实例化Executor,并且重写execute方法
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                //这里获取的handler是主线程的handler,可看part 1 (2)
                handler.post(command);
            }
        };
    }

  execute()方法接收一个Runnable对象,那么我们回到上面的

mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));

  可以看出这里实例化了一个ResponseDeliveryRunnable对象作为Runnable对象。而这里的ResponseDeliveryRunnable则是当前类Executor的一个内部类,实现了Runnable接口,我们来看看:

/**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        //构造方法
        ...
        @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);  // 1
            } else {
                mRequest.deliverError(mResponse.error);
            }
            ...
            }
       }
    }

  在上面,①号代码回调到了Request的deliverResponse方法,即此时通知了主线程,请求已经完成,此时在Request子类重写的deliverResponse方法则会调用onResponse()方法,那么我们的Activity就能方便调用解析好的请求结果了。

  到目前为止,关于Volley的源码解析完毕。

总结

  用google官方的一幅图来说明以上所说各个部分的关系(图片来自网络):

  蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络请求线程。读者可以根据这幅图所展示的关系,再仔细体会part 1-6所说的流程,那么就很容易理解了。最后感谢你的阅读。

时间: 2024-10-11 17:30:38

(干货) Android Volley框架源码详细解析的相关文章

Android网络通信Volley框架源码浅析(三)

尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析,相信大家对于Volley有了初步的认识,但是如果想更深入的理解,还需要靠大家多多看源码. 这篇文章中我们主要来研究一下使用Volley框架请求大量图片的原理,在Android的应用中,通过http请求获取的数据主要有三类: 1.json 2.xml 3.Image 其中json和xml的获取其实原理很简单,使用Volley获取感觉有点大财小用了,了解Volle

iOS开发之Masonry框架源码深度解析

Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁.Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束.本篇博客的主题不是教你如何去使用Masonry框架的,而是对Masonry框架的源码进行解析,让你明白Masonry是如何对NSLayoutConstraint进行封装的,以及Masonry框架中的各个部分所扮演的角色是什么样的.在Masonry框架中,仔细

Volley框架源码浅析(一)

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Voll

Volley框架源码浅析(二)

尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列 /** The cache triage queue. */ private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<

android 网络框架 源码分析

android 网络框架 源码分析 导语: 最近想开发一个协议分析工具,来监控android app 所有的网络操作行为, 由于android 开发分为Java层,和Native层, 对于Native层我们只要对linux下所有网络I/O接口进行拦截即可,对于java 层,笔者对android 网络框架不是很了解,所以这个工具开发之前,笔者需要对android 的网络框架进行一个简单的分析. 分析结论: 1. android 的网络框架都是基于Socket类实现的 2. java 层Socket

Android之Volley框架源码分析

临近毕业,各种事情各种忙.我也没有认真专注写过博客,最近仔细看了Volley框架的使用及其源码,思前想后,想挑战一下自己,还是写一篇博客来分享,如有错误,欢迎吐槽. Volley简介 网络请求是一个App很重要的一部分,android系统只是提供了一个平台,而android应用则是基于这个平台上进行展示数据,起到与用户进行交互的作用,数据来源于服务端,而二者之间必须通过互联网进行传输数据,在Android系统发布初期,很多开发者都是在Apache协会的Http协议的基础上进行网络请求方法的封装,

HashMap 源码详细解析 (JDK1.8)

概要 HashMap 最早出现在 JDK 1.2 中,底层基于散列算法实现.HashMap 允许 null 键和 null 值,在计算哈键的哈希值时,null 键哈希值为 0.HashMap 并不保证键值对的顺序,这意味着在进行某些操作后,键值对的顺序可能会发生变化.另外,需要注意的是,HashMap 是非线程安全类,在多线程环境下可能会存在问题. HashMap 底层是基于散列算法实现,散列算法分为散列再探测和拉链式.HashMap 则使用了拉链式的散列算法,并在 JDK 1.8 中引入了红黑

Android网络框架源码分析一---Volley

转载自 http://www.jianshu.com/p/9e17727f31a1?utm_campaign=maleskine&utm_content=note&utm_medium=mobile_author_hots&utm_source=recommendation 公司最近新起了一个项目,对喜欢尝鲜的我们来说,好处就是我们可以在真实的项目中想尝试一些新技术,验证想法.新项目对网络框架的选取,我们存在三种方案: 1.和我们之前的项目一样,使用Loader + HttpCli

Volley框架源码分析

Volley框架分析Github链接 Volley框架分析 Volley源码解析 为了学习Volley的网络框架,我在AS中将Volley代码重新撸了一遍,感觉这种照抄代码也是一种挺好的学习方式.再分析Volley源码之前,我们先考虑一下,如果我们自己要设计一个网络请求框架,需要实现哪些事情,有哪些注意事项? 我的总结如下: 需要抽象出request请求类(包括url, params, method等),抽象出request请求类之后,我们可以对其继承从而实现丰富的扩展功能. 需要抽象出resp