访问网络-------开源-Volley(Google亲儿子)

2023-11-06 05:40

本文主要是介绍访问网络-------开源-Volley(Google亲儿子),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Volley是Google推出的一个网络请求库,已经被放到了Android源码中,地址在这里,先看使用方法

复制代码
RequestQueue mRequestQueue = Volley.newRequestQueue(context);
JsonObjectRequest req = new JsonObjectRequest(URL, null,new Response.Listener<JSONObject>() {@Overridepublic void onResponse(JSONObject response) {try {VolleyLog.v("Response:%n %s", response.toString(4));} catch (JSONException e) {e.printStackTrace();}}}, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {VolleyLog.e("Error: ", error.getMessage());}});
mRequestQueue.add(req);
复制代码

 详细的使用方法就不说了,网上很多,可以看下这个,这里只大概介绍一下Volley的工作方法,就从上面的例子开始。

我们接触到的Volley的核心就两个,从名字就可以看出其用途。

  • RequestQueue
  • Request

前面我们看到RequestQueue是通过Volley的方法newRequestQueue获得的,Volley类的唯一作用就是获取RequestQueue的实例,而我们完全可以自己new RequestQueue,不知道为什么不把这两个类合并了。

复制代码
/*** 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.* @param stack An {@link HttpStack} to use for the network, or null for default.* @return A started {@link RequestQueue} instance.*/
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.htmlstack = 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);
}
复制代码

 HttpStack

在newRequestQueue里出现了几个重要的概念,首先可以看到newRequestQueue有一个重载方法,接收一个HttpStack的实例,HttpStack只有一个方法:performRequest,用来执行网络请求并返回HttpResponse,如果不传这个参数就根据API Level自己选择:

  • 当 API >= 9 即2.3及以后的系统使用HurlStack
  • 2.3以前的系统使用HttpClientStack

从这两个类的名字就大概知道了它们的区别了:HurlStack内部使用HttpURLConnection执行网络请求,HttpClientStack内部使用HttpClient执行网络请求,至于为什么么这样,可以自备梯子看这篇文章。

Network

Network是请求网络的接口,只有一个实现类BasicNetwork,只有一个方法performRequest,执行Request返回NetworkResponse。

NetworkHttpStack接口都只有一个方法,从方法的名字就可以看出它们的区别,Network.performRequest收Request参数返回om.android.volley.NetworkResponseHttpStack.performRequest返回org.apache.http.HttpResponse,层次更低,所以应该是Network.performRequest中调用HttpStack.performRequest执行实际的请求,并将HttpStack.performRequest返回的org.apache.http.HttpResponse封装成com.android.volley.NetworkResponse返回。

Cache

Volley中使用Cache接口的子类DiskBasedCache做缓存,这是一个文件缓存,Cache接口有一个initialize方法用来初始化缓存,这个方法可能会执行耗时操作,需要在后台线程中执行,看DiskBasedCache可以知道,当它将缓存写到文件时,在文件的头部写了一些Header信息,在initialize时就会将这些Header信息读入内存中。

Request类中有一个方法叫parseNetworkResponseRequest的子类会覆写这个方法解析网络请求的结果,在这个方法中会调用

return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));

 返回Response<T>,并通过HttpHeaderParse.parseCacheHeaders解析Cache.Entity,即生成缓存对象,在parseaCheHeaders中会根据网络请求结果中的Header中的ExpiresCache-Control等信息判断是否需要缓存,如果不需要就返回null不缓存。

当对请求做了缓存后,没网的情况下也可以得到数据。

Cache还有一个子类叫NoCache,get方法返回Null,其他方法都是空的,所以使用NoCache就表示不用缓存。

RequestQueue

 

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();}
}
复制代码

 RequestQueue是请求队列,负责分发请求,取缓存或读网络,所以其构造函数中需要一个Cache对象和一个Network对象,还有一个ResponseDelivery对象用于派发结果。

新建RequestQueue后要调用它的start方法,在start中会新建一个CacheDispatcher和几个NetworkDispatcher分别处理缓存与网络请求

通过RequestQueue的add方法添加请求:

复制代码
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");// 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. 判断当前的Request是否使用缓存,如果不使用缓存直接加入网络请求队列mNetworkQueue返回
  2. 如果使用缓存,判断之前是否有执行相同的请求且还没有返回结果
  3. 如果第2步的判断是true,将此请求加入mWaitingRequests队列,不再重复请求,在上一个请求返回时直接发送结果
  4. 如果第2步的判断是false,将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来当下个请求来时做第2步中的判断

RequestQueue.add的任务就是这些,可以看到,它并没有执行任何实际的请求操作,包括判断缓存与请求网络,直正的操作是接下来要说的两个类执行的。

CacheDispatcher

mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);

 在RequestQueue.add方法中,如果使用缓存直接就将Request放入缓存队列mCacheQueue中了,使用mCacheQueue的位置就是CacheDispatcher,CacheDispatcher的构造函数中传入了缓存队列mCacheQueue、网络队列mNetworkQueue、缓存对象mCache及结果派发器mDelivery。

CacheDispatcher继承自Thread,当被start后就执行它的run方法,代码不贴了,主要完成以下工作:

  1. 从mCacheQueue取请求Request
  2. 每个Request都可以从中得到CacheKey,看对应的CacheKey在缓存mCache中是否存在
  3. 如果缓存不存在就加到网络队列mNetworkQueue中继续取下一个请求
  4. 如果缓存存在,判断是否过期
  5. 如果过期了就加入网络队列mNetworkQueue中继续取下一个请求
  6. 如果没过期,看是否需要刷新
  7. 如果不需要刷新,直接派发结果
  8. 如果需要刷新,调用mDelivery.postResponse派发结果,并将Request加入网络队列重新请求最新数据
复制代码
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() {@Overridepublic void run() {try {mNetworkQueue.put(request);} catch (InterruptedException e) {// Not much we can do about this.
        }}
});
复制代码

NetworkDispatcher

NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);

 NetworkDispatcher的工作方法同CacheDispatcher一样,继承自Thread,当被start后,不停地从mNetworkQueue取请求,然后通过Network接口请求网络。

当拿到请求结果后,如果服务器返回304(自上次请求后结果无变化)并且结果已经通过缓存派发了(即这次是读了缓存后的Refresh),那么什么也不做,否则调用Request的parseNetworkResponse解析请求结果,如果需要进行缓存,并派发结果

ResponseDelivery

派发请求结果的接口,有一个子类ExecutorDelivery执行实际操作,构造ExecutorDelivery的对象时需要一个Handler对象,当向ExecutorDelivery请求派发结果时会向这个Handler post消息。

Request

Request表示一个请求,支持四种优先级:LOW、NORMAL、HIGH、IMMEDIATE,主要有以下几个方法:

  • getHeaders 获取请求Http Header列表
  • getBodyContentType 请求类型,如application/x-www-form-urlencoded; charset=utf-8
  • getBody 将要发送的POST或PUT请求的内容
  • getParams,获取POST或PUT请求的参数,如果重写getBody的话这个就用不到了
  • parseNetworkResponse 将请求结果解析成需要的类型,将NetworkResponse解析成Response<T>,NetworkResponse中的data成员即网络请求结果为byte[]
  • deliverResponse 子类需要实现,用于将结果派发至Listener

StringRequest将结果转换成了String并Deliver至Response.Listener

复制代码
@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));
}
复制代码

 JsonRequest<T>

可以在发送请求时同时发送一个JSONObject参数,覆写了getBody

复制代码
@Override
public byte[] getBody() {try {return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);} catch (UnsupportedEncodingException uee) {VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",mRequestBody, PROTOCOL_CHARSET);return null;}
}
复制代码

 mRequestBody是构造函数里传进来的一个String,一般是JSONObject.toString()。

JsonObjectRequest

继承自JSONRequest<T>,将请求结果解析成JSONObject

复制代码
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,Listener<JSONObject> listener, ErrorListener errorListener) {super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,errorListener);
}@Override
protected Response<JSONObject> rkResponse(NetworkResponse response) {try {String jsonString =new String(response.data, HttpHeaderParser.parseCharset(response.headers));return Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));} catch (UnsupportedEncodingException e) {return Response.error(new ParseError(e));} catch (JSONException je) {return Response.error(new ParseError(je));}
}
复制代码

 JsonArrayRequest

同JsonObjectRequest一样,继承自JSONRequest<T>,只是把结果解析成JSONArray。

ClearCacheRequest

Hack性质的请求,用于清除缓存,设置为最高优先级IMMEDIATE,执行请求时会调用Request.isCanceled判断请求是否被取消掉了,就在这里清除了缓存

复制代码
@Override
public boolean isCanceled() {// This is a little bit of a hack, but hey, why not.
    mCache.clear();if (mCallback != null) {Handler handler = new Handler(Looper.getMainLooper());handler.postAtFrontOfQueue(mCallback);}return true;
}
复制代码

Cancel

RequestQueue同样提供了取消请求的方法。通过它的cancelAll方法。

复制代码
/*** A simple predicate or filter interface for Requests, for use by* {@link RequestQueue#cancelAll(RequestFilter)}.*/
public interface RequestFilter {public boolean apply(Request<?> request);
}/*** Cancels all requests in this queue for which the given filter applies.* @param filter The filtering function to use*/
public void cancelAll(RequestFilter filter) {synchronized (mCurrentRequests) {for (Request<?> request : mCurrentRequests) {if (filter.apply(request)) {request.cancel();}}}
}/*** Cancels all requests in this queue with the given tag. Tag must be non-null* and equality is by identity.*/
public void cancelAll(final Object tag) {if (tag == null) {throw new IllegalArgumentException("Cannot cancelAll with a null tag");}cancelAll(new RequestFilter() {@Overridepublic boolean apply(Request<?> request) {return request.getTag() == tag;}});
}
复制代码

 通过给每个请求设置一个Tag,然后通过cancelAll(final Object tag)就可以取消对应Tag的请求,也可以直接使用RequestFilter

图片加载

通过ImageRequest、ImageLoader和NetworkImageView等类,Volley还可用于加载图片,通过加锁实现了同时只解析一张图片,同时只解析一张,而不是只加载一张,网络请求还是跟普通的请求一样,返回的是byte数组,解析指byte[]->Bitmap,由于请求结果是byte[],大图应该很容易内存溢出,而且不支持本地图片,所以不考虑使用,略过。

总结

Volley的扩展应该还是比较容易的,网络已经有各种版本扩展了,像Cache,Request等都是提供的接口,很容易有自己的实现,比如实现GsonRequest用于使用Gson解析返回的json结果:

复制代码
protected Response<T> parseNetworkResponse(NetworkResponse response) {try {String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));} catch (UnsupportedEncodingException e) {return Response.error(new ParseError(e));} catch (JsonSyntaxException e) {return Response.error(new ParseError(e));}
}
复制代码

 Volley被设计用于小的网络请求,所以像上传下载大文件什么的就不适合了,虽然网上已有相应的扩展,而且原生是没有文件上传的。

Volley还有NetImageView,ImageLoader等和加载图片相关的,不过我个人习惯了使用UniversalImageLoader

虽然网上都说Volley速度快,易于扩展,也给出了对比数据,但总归是要自己手工扩展,也没看出特别大的优势,和Android-Async-Http相比有一点不同就是2.3后使用了官方建议的HttpUrlConnection

还有一点最主要的应该就是Volley的缓存方法了,根据进行请求时服务器返回的缓存控制Header对请求结果进行缓存,下次请求时判断如果没有过期就直接使用缓存加快响应速度,如果需要会再次请求服务器进行刷新,如果服务器返回了304,表示请求的资源自上次请求缓存后还没有改变,这种情况就直接用缓存不用再次刷新页面,不过这要服务器支持了。

当对上次的请求进行缓存后,在下次请求时即使没有网络也可以请求成功,关键的是,缓存的处理对用户完全是透明的,对于一些简单的情况会省去缓存相关的一些事情

这篇关于访问网络-------开源-Volley(Google亲儿子)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/copy_yuan/article/details/51035461
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/354770

相关文章

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

本地搭建DeepSeek-R1、WebUI的完整过程及访问

《本地搭建DeepSeek-R1、WebUI的完整过程及访问》:本文主要介绍本地搭建DeepSeek-R1、WebUI的完整过程及访问的相关资料,DeepSeek-R1是一个开源的人工智能平台,主... 目录背景       搭建准备基础概念搭建过程访问对话测试总结背景       最近几年,人工智能技术

Ollama整合open-webui的步骤及访问

《Ollama整合open-webui的步骤及访问》:本文主要介绍如何通过源码方式安装OpenWebUI,并详细说明了安装步骤、环境要求以及第一次使用时的账号注册和模型选择过程,需要的朋友可以参考... 目录安装环境要求步骤访问选择PjrIUE模型开始对话总结 安装官方安装地址:https://docs.

解读静态资源访问static-locations和static-path-pattern

《解读静态资源访问static-locations和static-path-pattern》本文主要介绍了SpringBoot中静态资源的配置和访问方式,包括静态资源的默认前缀、默认地址、目录结构、访... 目录静态资源访问static-locations和static-path-pattern静态资源配置

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

使用Python实现批量访问URL并解析XML响应功能

《使用Python实现批量访问URL并解析XML响应功能》在现代Web开发和数据抓取中,批量访问URL并解析响应内容是一个常见的需求,本文将详细介绍如何使用Python实现批量访问URL并解析XML响... 目录引言1. 背景与需求2. 工具方法实现2.1 单URL访问与解析代码实现代码说明2.2 示例调用

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int