文档章节

【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

htq
 htq
发布于 2016/07/26 09:41
字数 4827
阅读 11
收藏 0
点赞 1
评论 0

通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法。如StringRequest用来请求一段文本信息,JsonRequest(JsonObjectRequest、JsonArrayRequest)用于请求一条JSON格式的数据,ImageRequest则是用于请求网络上的图片资源。但是我们知道在网络上传输的轻量级数据还包括另外一种即XML。但是Volley中目前还没提供用于请求XML格式的类,那我们如果我们要请求的是一段XML格式数据该怎们办?

答案很简单,自己动手写,还记得在初识Volley的博客中讲到的Volley的基本特点之一吗:可扩展性强。Volley提供了非常强的扩展机制,使得我们可以很轻松地定制出任意类型的Request。

一定制自己的Request请求

定制XMLRequest:去扩展不是自己开发的框架中的某些功能不是一件很容易的事,因为你很可能不知道从哪个地方作为切入点,因此我们首先要做的就是把谷歌官方写的一些与自己要定制的类功能相似的类好好研究一下,看能否看懂然后自己模仿。那么自然首先想到去研究一下StringRequest类的源码,代码如下:

/**
 * A canned request for retrieving the response body at a given URL as a String.
 */
public class StringRequest extends Request<String> {
    private final Listener<String> mListener;

    /**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void deliverResponse(String response) {
        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));
    }
}
可以看到即使加上注释代码也没多少,我们首先从类的定义开始分析:

public class StringRequest extends Request<String>
可以看到StringRequest继承自Request,而且提供了一个泛型参数String用来表示该类主要用来处理String类型的数据。这也是为何StringRequest中的代码如此简单的原因,因为复杂的网络请求的绝大部分公共功能都在抽象类Request类中实现了,我们只需要继承自它然后重写一些属于自己的特定的方法即可。因此我们知道如果我们要定制一个XMLRequest通过样也需要继承自Request,然后指定泛型参数为XML解析器即可。

接下来是其构造器,这个在初识Volley的博客中已经讲过,因此不再赘述。

然后是两个重写方法,这也是我们关注的重点:

@Override
    protected void deliverResponse(String response) {
        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));
    }

第一个为protected void deliverResponse(String response) ,它是在其父类Request中定义的一个抽象方法,即所有继承自Request的子类必须重写它,其定义如下:

abstract protected void deliverResponse(T response);
可以看到参数为传入的泛型参数,该函数的功能即为当从服务器端获取数据成功时会自动回调该方法。采用的是观察者模式,即先定义一个泛型的Listener对象,然后再该方法中调用该对象的onResponse(response)方法。

第二个为 protected Response<String> parseNetworkResponse(NetworkResponse response),它同样是在其父类Request中定义的一个抽象方法,即所有继承自Request的子类必须重写它,其定义如下:

abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
可以看到参数为传入的泛型参数,顾名思义该函数的功能即为将从服务器端获取到的数据进行解析,是整个类中最核心的部分,也是不同功能的Request必须定制重写的一个方法,服务器端返回数据用NetworkResponse这个类来表示,其定义如下:

public class NetworkResponse {
    /**
     * Creates a new network response.
     * @param statusCode the HTTP status code
     * @param data Response body
     * @param headers Headers returned with this response, or null for none
     * @param notModified True if the server returned a 304 and the data was already in cache
     */
    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified) {
        this.statusCode = statusCode;
        this.data = data;
        this.headers = headers;
        this.notModified = notModified;
    }

    public NetworkResponse(byte[] data) {
        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
    }

    public NetworkResponse(byte[] data, Map<String, String> headers) {
        this(HttpStatus.SC_OK, data, headers, false);
    }

    /** The HTTP status code. */
    public final int statusCode;

    /** Raw data from this response. */
    public final byte[] data;

    /** Response headers. */
    public final Map<String, String> headers;

    /** True if the server returned a 304 (Not Modified). */
    public final boolean notModified;
}

重点关注byte[] data这个成员属性, 它是用来存储从服务器端获取的数据,/** Raw data from this response. */,即从服务器端获取到数据将以字节的形式存放在data字节数组中。因此我们需要获取怎样类型的数据就只需要对该数组中的内容进行相应操作即可,如在上述的StringRequest中因为要获取的是String类型的数据,因此我们需要将其以字符串的形式表示, parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));然后调用Response.success()方法将其返回return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));。其中success返回的是代泛型参数的Response对象,其定义如下:

/** Returns a successful response containing the parsed result. */
    public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
        return new Response<T>(result, cacheEntry);
    }


讲到这我们基本上就从源码的角度彻底理解了StringRequest类,那么接下来我们就按照上述我为大家分析的步骤模仿写一个XMLRequest,代码如下:

public class XMLRequest extends Request<XmlPullParser> {//继承自Request类,指定泛型参数为XML解析器

	private final Listener<XmlPullParser> mListener;//指定监听器的泛型参数为XML解析器

	public XMLRequest(int method, String url, Listener<XmlPullParser> listener,
			ErrorListener errorListener) {
		super(method, url, errorListener);
		mListener = listener;
	}

	public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {
		this(Method.GET, url, listener, errorListener);
	}

	@Override
	protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
		try {
			String xmlString = new String(response.data,
					HttpHeaderParser.parseCharset(response.headers));
			XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
			XmlPullParser xmlPullParser = factory.newPullParser();
			xmlPullParser.setInput(new StringReader(xmlString));
			return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));//返回包含该xml解析器的Response对象
		} catch (UnsupportedEncodingException e) {
			return Response.error(new ParseError(e));
		} catch (XmlPullParserException e) {
			return Response.error(new ParseError(e));
		}
	}

	@Override
	protected void deliverResponse(XmlPullParser response) {
		mListener.onResponse(response);
	}

}
如果你了解XML解析的话那么上述代码对你而言将会相当的简单。下面我们就来使用这个我们自定义的XMLRequest。

XMLRequest xmlRequest = new XMLRequest(
		"http://flash.weather.com.cn/wmaps/xml/china.xml",
		new Response.Listener<XmlPullParser>() {
			@Override
			public void onResponse(XmlPullParser response) {
				try {
					int eventType = response.getEventType();
					while (eventType != XmlPullParser.END_DOCUMENT) {
						switch (eventType) {
						case XmlPullParser.START_TAG:
							String nodeName = response.getName();
							if ("city".equals(nodeName)) {
								String pName = response.getAttributeValue(0);
								Log.d("TAG", "pName is " + quName);
							}
							break;
						}
						eventType = response.next();
					}
				} catch (XmlPullParserException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}, new Response.ErrorListener() {
			@Override
			public void onErrorResponse(VolleyError error) {
				Log.e("TAG", error.getMessage(), error);
			}
		});
mQueue.add(xmlRequest);

运行结果如下:


讲到这基本上就把Volley的用法及其扩展全部讲完了,相信大家对Volley使用已经相当熟练了,但这还不够,接下来我们不再是从单个类的源码角度来解析Volley,而是从整个框架体系角度来解析Volley,让你彻底了解Volley的整个框架体系。


二Volley框架源码剖析

首先我们来看一下谷歌官方文档中Volley的框架图:


从这个图可以看到整个框架体系包括至少三个线程:主线程(main thread),缓存调度线程(cache thread),网络调度线程(network thread)。那么我们首先从主线程入手。

可以看到在主线程中会根据优先级将请求添加到缓存队列。所谓缓存队列就是前面我们每次使用Volley时都要创建的RequestQueue对象。那么我们首先来看一下其源代码:

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) {
            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 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时调用的即Volley.newRequestQueue(context)。可以看到在该方法中事实上是调用的带两个参数的重载方法,即第一个方法。可以看到首先指定了硬盘缓存的位置为data/data/package_name/cache/volley/...,然后判断SDK的版本号,如果大于等于9则会创建一个HurlStack(),否则创建一个HttpClientStack(),而实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的,至于为何这么选择在我的博文 【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法已介绍过,在此不再赘述。创建好了HttpStack之后,接下来又创建了一个Network对象(具体实现类是BasicNetwork),它是用于根据传入的HttpStack对象来处理网络请求的,然后创建一个请求队列RequestQueue调用其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();
        }
    }
可以看到,该方法代码逻辑非常简单,创建了一个CacheDispatcher和4个NetworkDispatcher个对象,然后调用start(),这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。可以看到在CacheDispatcher的构造器中传入了缓存请求队列(mCacheQueue),网络请求队列(mNetworkQueue),硬盘缓存对象(DiskBasedCache),结果分发器(mDelivery)。之所以也传入网络请求队列是因为一部分缓存请求可能已经过期了,这时候需要重新从网络获取。NetworkDispatcher除了缓存请求队列没有传入,其他跟CacheDispatcher一样。到这里RequestQueue的任务就完成了,以后有请求都会交由这些dispatcher线程处理。

创建了请求队列之后我们只需要构建出相应的Request,然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作,那么接下来我们来看一下请求添加

即add()的源码:

/**
     * Adds a Request to the dispatch queue.
     * @param request The request to service
     * @return The passed-in request
     */
    public Request add(Request 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");

        // 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.
                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;
        }
    }
可以看到add的大致逻辑如下:

1.将请求加入mCurrentRequests集合(注意此行为是同步的)
2.为请求添加序列号
3.判断是否应该缓存请求,如果不需要,加入网络请求队列
4.如果有相同请求正在被处理,加入到相同请求等待队列中,否则加入缓存请求队列。
通过上述分析我们可以知道add方法仅仅只起到添加请求的作用,而请求的处理是CacheDispatcher和NetworkDispatcher来完成的。这也正好与上述给的Volley的框架流程图相符。我们来看一下其源码:

public class CacheDispatcher extends Thread {

    private static final boolean DEBUG = VolleyLog.DEBUG;

    /** The queue of requests coming in for triage. */
    private final BlockingQueue<Request> mCacheQueue;

    /** The queue of requests going out to the network. */
    private final BlockingQueue<Request> mNetworkQueue;

    /** The cache to read from. */
    private final Cache mCache;

    /** For posting responses. */
    private final ResponseDelivery mDelivery;

    /** Used for telling us to die. */
    private volatile boolean mQuit = false;

    /**
     * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param cacheQueue Queue of incoming requests for triage
     * @param networkQueue Queue to post requests that require network to
     * @param cache Cache interface to use for resolution
     * @param delivery Delivery interface to use for posting responses
     */
    public CacheDispatcher(
            BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @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();

        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request request = mCacheQueue.take();
                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");
                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.
                    mDelivery.postResponse(request, response);
                } else {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}
重点关注run()方法,因为CacheDispatcher继承自Thread,其重要的逻辑处理都是在run()方法中完成的。可以看到在run()方法中存在一个while(true)死循环,即

缓存线程是一直在运行的,在该while循环中的处理过程大致如下:

1首先从缓存中取出一个请求,如果结果为空,则把该请求加入到网络请求中,否则对缓存结果进行过期判断。

2如果过期了,则加入网络请求队列。如果没有过期,那么通过request.parseNetworkResponse方法将硬盘缓存中的数据构造为Response对象(Request的parseNetworkResponse是抽象的,需要子类重写)。

在来看看网络请求线程的源码:

public class NetworkDispatcher extends Thread {
    /** The queue of requests to service. */
    private final BlockingQueue<Request> mQueue;
    /** The network interface for processing requests. */
    private final Network mNetwork;
    /** The cache to write to. */
    private final Cache mCache;
    /** For posting responses and errors. */
    private final ResponseDelivery mDelivery;
    /** Used for telling us to die. */
    private volatile boolean mQuit = false;

    /**
     * Creates a new network dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param queue Queue of incoming requests for triage
     * @param network Network interface to use for performing requests
     * @param cache Cache interface to use for writing responses to cache
     * @param delivery Delivery interface to use for posting responses
     */
    public NetworkDispatcher(BlockingQueue<Request> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true) {
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                // Tag the request (if API >= 14)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                }

                // Perform the network request.
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back.
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }

    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }
}
同样我们也只需要关注run()方法中的代码,可以看到在该方法中同样存在一个while(true)死循环,即网络请求线程也是一直在运行的,在该while循环中的处理过程大致和缓存线程相同,也是构造Response对象,然后交由ResponseDelivery处理,但是这里的Response对象是通过NetworkResponse转化的,而这个NetworkResponse是从网络获取的,这里最核心的一行代码就是:

// Perform the network request.
 NetworkResponse networkResponse = mNetwork.performRequest(request);

即执行网络请求,在这个perfromRequest中会调用HttpClient或HttpUrlConnection去执行网络请求,然后将从服务器端获取到的数据构造成一个NetWorkResponse对象返回。

4请求结果的分发与处理:

在NetworkDispatcher中收到了NetworkResponse这个返回值后又会调用Request的parseNetworkResponse()方法来解析NetworkResponse中的数据,以及将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。还记得我们上述自定义Request的方式吗?其中parseNetworkResponse()这个方法就是必须要重写的。
在解析完了NetworkResponse中的数据之后,又会调用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()方法中传入了一个ResponseDeliveryRunnable对象,这样做是为了保证该对象中的run()方法是在主线程当中运行的了,我们来看一下其源码:

private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        }

        @SuppressWarnings("unchecked")
        @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();
            }
       }
    }
}
同样重点关注run方法中的代码,我们可以看到如果请求成功会调用即

if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);}
而deliverResponse(mResponse.result)这个方法就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都会回调到这个方法中,所以我们需要重写该方法,在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。

至此Volley的整个框架体系就解析完了,我们来总结一下:

1首先在主线程中创建一个请求队列:Volley.newRequestQueue(context)然后通过add()添加一条网络请求。

2这条请求会被先添加到缓存队列,如果在缓存队列中找到该请求则读取缓存解析,然后将解析结果返回给主线程

3如果没找到该请求则将该请求加入网络请求队列,发送HTTP请求,获取服务器端返回的结果,解析之后写入缓存,然后将解析结果返回主线程。

如果你再仔细看一下上述Volley框架体系结构图,你会看到上述三个步骤正好完全对应谷歌官方文档的架构体系流程图。




本文转载自:http://blog.csdn.net/htq__/article/details/51119948

共有 人打赏支持
htq

htq

粉丝 19
博文 67
码字总数 1007
作品 3
武汉
volley框架下发送和读取cookie

本文为原创,转载请注明出处,否则将依法追究版权 我们平时开发android应用都需要用到网络技术,通常采用http协议来发起请求并接受网络数据。android系统提供两种方式进行http通信:HttpURL...

刘小米 ⋅ 2014/12/29 ⋅ 11

Android 中判断网络连接和GPS是否可用及HTTPCLIENT使用

大家知道Google支持和发布的Android移动操作系统,主要是为了使其迅速占领移动互联网的市场份额,所谓移动互联网当然也是互联网了,凡是涉及互联网的任何软件任何程序都少不了联网模块的开发...

xztelecomlcs ⋅ 2017/03/14 ⋅ 0

网络请求框架---Volley

去年的Google I/O大会为android开发者带来了一个网络请求框架,它的名字叫做Volley。Volley诞生的使命就是让Android的网络请求更快,更健壮,而且它的网络通信的实现是基于HttpURLConnection...

Jack_1900 ⋅ 2014/08/01 ⋅ 2

volley源码学习

volley源码学习 之前一直对于源码学习抱着一种又爱又恨的心情。爱的是因为知道源码有一些特别好的设计思路,可以让自己借鉴,而且对于设计模式来说是最好的实战场。那为啥还会恨呢,曾经很多...

xuelang ⋅ 2017/11/26 ⋅ 0

Android-网络通信框架Volley使用详解

1 Volley发送get请求: public void getJson() { String url = "http://"+host+":8080/web/json.jsp?username=xjs&password=123456"; mQueue.add(new JsonObjectRequest(Method.GET, url, n......

新年 ⋅ 2015/06/11 ⋅ 0

Volley 基本用法

GitHubDemo https://github.com/smanikandan14/Volley-demo#handling-error-codes Volley源码 https://github.com/mcxiaoke/android-volley Google IO2013网络框架Volley 演讲PDF http://do......

小强强强 ⋅ 2015/03/23 ⋅ 0

Volley 源码解析

本文为 Android 开源项目源码解析 中 Volley 部分 项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo 分析者:grumoon,校对者:huxian99、Trinea,校对状态:完成 1. 功能介绍...

程序袁_绪龙 ⋅ 2015/12/17 ⋅ 1

Android 网络通信框架Volley简介(Google IO 2013)

什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,今年的Google I/O 2013上,Vol...

mingxun ⋅ 2013/11/06 ⋅ 0

Android 网络通信框架Volley简介(Google IO 2013)

什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,今年的Google I/O 2013上,Vol...

亭子happy ⋅ 2013/10/21 ⋅ 1

Android 网络通信框架Volley简介(Google IO 2013)

Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v=yhv8l9F44qo&feature=playerembedded 1. 什么是Volley 在这之前,我们在程序中需......

今日竹石 ⋅ 2015/02/28 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何使用serverchan微信推送告警

之前实现推送告警信息到微信的方法有如下几种: 1、通过企业公众号实现----收费: 2、通过QQ邮箱,在微信平台上开启收到邮件进行提醒; 3、第三方告警平台API,一般也是收费的; 不过最近看文...

问题终结者 ⋅ 28分钟前 ⋅ 0

TCP的RPC

RPC就是远程方法调用(Remote Process Call ),包含了客户端和服务端,涉及了对象的序列化传输。 1.服务端启动,注册远程调用的类2.客户端发送请求信息包含类、方法、参数的一些信息、序列化传...

Cobbage ⋅ 48分钟前 ⋅ 0

IOS-UI UI初步代码布局添加事件

ISO开发界面,UI是必须学习的一部分,其实很早之前想学来了,一直没有沉下心来学习。看到IOS的代码风格和布局就别扭的不行,跟java代码和android布局比较显得不是那么方便,所以一直到现在。...

京一 ⋅ 59分钟前 ⋅ 0

浅谈OpenDaylight的二次开发

OpenDaylight作为一款开源SDN网络控制器,依托于强大的社区支持以及功能特性,成为了目前主流的SDN网络控制器开发平台。在比较稳定的OpenDaylight Helium版本中,已经为开发者提供了大量的网...

wangxuwei ⋅ 今天 ⋅ 0

API 开发中可选择传递 token 接口遇到的一个坑

在做 API 开发时,不可避免会涉及到登录验证,我使用的是jwt-auth 在登录中会经常遇到一个token过期的问题,在config/jwt.php默认设置中,这个过期时间是一个小时,不过为了安全也可以设置更...

等月人 ⋅ 今天 ⋅ 0

Java NIO之文件处理

程序要操作本地操作系统的一个文件,可以分为以下三个部分: 对文件位置的操作 对文件的操作 对文件内容的操作 其中,对文件内容的操作在 Java NIO之Channel 中已经有了介绍,通过FileChann...

士别三日 ⋅ 今天 ⋅ 0

Maven的pom.xml配置文件详解

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.......

小海bug ⋅ 今天 ⋅ 0

解决httpclient超时设置不生效的问题

最近公司有项目需要通过http调用第三方服务,且第三方服务偶有超时,故需要设置一定的超时时间防止不响应的情况出现。 初始设置如下: [java] view plain copy //超时设置 RequestConfig re...

Mr_Tea伯奕 ⋅ 今天 ⋅ 0

过滤器Filter和拦截器HandlerInterceptor

过滤器 依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要...

hutaishi ⋅ 今天 ⋅ 0

Redis入门详解(转)

Redis入门详解 Redis简介 Redis安装 Redis配置 Redis数据类型 Redis功能 持久化 主从复制 事务支持 发布订阅 管道 虚拟内存 Redis性能 Redis部署 Redis应用场景 Redis总结 Redis简介: Redi...

xiaoyaoyoufang ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部