文章目录
volley优势:
1,使用了线程池,支持多个并发网络请求链接,可以高强度密集小数据量网络请求;
2,支持请求优先级,可以进行定制,设置队列最大请求数等;
3,支持取消网络;可以取消单个请求,也可以设置要取消的请求块或范围。
4,自动调度网络请求,已经做了封装,可以在主线程发起请求会自动切换到子线程,子线程拿到响应会自动切换到主线程,回调到填充UI;
volley劣势:
Volley
不适合大型下载或流媒体操作,因为Volley在解析期间将所有响应保存在内存中。对于大型下载操作,请考虑使用类似的替代方法DownloadManager
。
可以通过创建RequestQueue
并传递 Request
对象来使用Volley
。
RequestQueue
所做的工作:1管理子线程的网络请求的执行操作,2写入原始响应到缓存并从写入的缓存中读取响应内容,3解析响应(其实这第三步是异步回调来完成的,严格来说不能算是RequestQueue的工作)。
Requests do the parsing of raw responses and
Volley takes care of dispatching the parsed response back to the main thread for delivery.
Requests做原始响应,Volley讲解析后的响应分发给主线程进行传递。
Volley总是在主线程上提供已解析的响应。
最简单的用法-发起网络请求
java:
final TextView mTextView = (TextView) findViewById(R.id.text);
// ...
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
mTextView.setText("Response is: "+ response.substring(0,500));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("That didn't work!");
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
kotlin:
val textView = findViewById<TextView>(R.id.text)
// ...
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "http://www.google.com"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the first 500 characters of the response string.
textView.text = "Response is: ${response.substring(0, 500)}"
},
Response.ErrorListener { textView.text = "That didn't work!" })
// Add the request to the RequestQueue.
queue.add(stringRequest)
原理分析
RequestQueue
调用add()
方法即queue.add(request)
,就可以将这个请求加入到线程池的中,在线程池的管道中排序进行移动、获取服务以及解析原声响应并传递到主线程。
当调用add()
方法时,Volley运行一个缓存处理线程和一个网络分发线程池。
当您向队列添加请求时,该请求会被缓存线程拾取并进行分类:如果请求可以从缓存中获得服务,则缓存响应将在缓存线程上进行解析,并且解析后的响应将传递到主线程上。如果无法从缓存中为请求提供服务,则将其置于网络请求队列中。第一个可用的网络线程从队列中取得请求,执行HTTP事务,解析工作线程上的响应,将响应写入缓存,并将解析的响应发送回主线程以进行传递。
请注意,阻塞I / O、解析/解码等昂贵耗时的操作是在工作线程上完成的。可以从任何线程添加请求,但响应最终都会传递到主线程上。
取消请求
要取消请求,使用RequestQueue
对象调用cancel()方法,来取消Request
请求。一旦取消,Volley就永远不会调用当前请求的响应处理产需。在实际开发中,可以在onStop()
方法中取消所有待处理的请求。
所以,必须要知道现在正在进行的请求是哪个,也就是说必须跟踪所有正在进行的请求,以便能够在适当时间取消他们。
volley提供了一种简便的办法来进行跟踪:将标记对象与每个请求相关联,也就是说给每个请求设置一个标记tag
,然后,可以使用此标记取消请求,可以取消一个请求,也可以取消一大块请求,比如取消当前页面所有的待处理的请求。
例如,可以将所有的请求的tag
设置为Activity
,在onStop()
方法中调用requestQueue.cancelAll(this)
来取消当前页面所有的待处理的网络请求。
两步走
1,设置标记
kotlin:
val TAG = "MyTag"
val stringRequest: StringRequest // Assume this exists.
val requestQueue: RequestQueue? // Assume this exists.
// Set the tag on the request.
stringRequest.tag = TAG
// Add the request to the RequestQueue.
requestQueue?.add(stringRequest)
java
public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue; // Assume this exists.
// Set the tag on the request.
stringRequest.setTag(TAG);
// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
2,根据标记取消请求
kotlin
protected fun onStop() {
super.onStop()
requestQueue?.cancelAll(TAG)
}
java:
@Override
protected void onStop () {
super.onStop();
if (mRequestQueue != null) {
mRequestQueue.cancelAll(TAG);
}
}
设置RequestQueue
上面是最简单的使用volley发起网络请求,Volley.newRequestQueue
来实例化RequestQueue
,线程池支持的最大并发请求数量、缓存大小、线程池管道请求排队、网络请求超时时间等使用的都是volley默认的设置,如果需要更改设置,可以对RequestQueue
进行设置。
如果只进行一次请求,那么也不用进行这些设置,直接通过默认设置进行使用就足够了。
RequestQueue
一般推介使用単例模式创建。
设置网络和缓存
重复讲一下,RequestQueue
做两件事情:1,执行网络请求的传输;2,操作处理缓存;Volley toolbox
包中提供了标准实现可用:DiskBasedCache
提供带有内存索引的单文件响应缓存,BasicNetwork
根据首选的HTTP客户端提供网络传输。BasicNetwork
是Volley的默认网络实现。BasicNetwork
必须通过HTTP客户端初始化,来保证APP可以连接到网络. 通常这个客户端是一个HttpURLConnection。
设置RequestQueue的步骤:
java:
RequestQueue mRequestQueue;
// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());
// Instantiate the RequestQueue with the cache and network.
mRequestQueue = new RequestQueue(cache, network);
// Start the queue
mRequestQueue.start();
String url ="http://www.example.com";
// Formulate the request and handle the response.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Do something with the response
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Handle error
}
});
// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
// ...
kotlin:
// Instantiate the cache
val cache = DiskBasedCache(cacheDir, 1024 * 1024) // 1MB cap
// Set up the network to use HttpURLConnection as the HTTP client.
val network = BasicNetwork(HurlStack())
// Instantiate the RequestQueue with the cache and network. Start the queue.
val requestQueue = RequestQueue(cache, network).apply {
start()
}
val url = "http://www.example.com"
// Formulate the request and handle the response.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
// Do something with the response
},
Response.ErrorListener { error ->
// Handle error
textView.text = "ERROR: %s".format(error.toString())
})
// Add the request to the RequestQueue.
requestQueue.add(stringRequest)
// ...
使用単例模式
如果应用程序需要不断的使用网络,RequestQueue
这个对象的获取可以使员工単例模式,来保证整个APP存活期间,只有一个对象,来提高效率。当然也可以子类化Application
,然后在Application.onCreate()
中初始化RequestQueue
。
一个关键概念是RequestQueue
必须使用Application
上下文实例化 ,而不是Activity
上下文。这可确保在RequestQueue
应用程序的生命周期内持续使用,而不是每次重新创建活动时重新创建(例如,当用户旋转设备时)。
例如
kotlin:
class MySingleton constructor(context: Context) {
companion object {
@Volatile
private var INSTANCE: MySingleton? = null
fun getInstance(context: Context) =
INSTANCE ?: synchronized(this) {
INSTANCE ?: MySingleton(context).also {
INSTANCE = it
}
}
}
val imageLoader: ImageLoader by lazy {
ImageLoader(requestQueue,
object : ImageLoader.ImageCache {
private val cache = LruCache<String, Bitmap>(20)
override fun getBitmap(url: String): Bitmap {
return cache.get(url)
}
override fun putBitmap(url: String, bitmap: Bitmap) {
cache.put(url, bitmap)
}
})
}
val requestQueue: RequestQueue by lazy {
// applicationContext is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
Volley.newRequestQueue(context.applicationContext)
}
fun <T> addToRequestQueue(req: Request<T>) {
requestQueue.add(req)
}
}
java:
public class MySingleton {
private static MySingleton mInstance;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static Context mCtx;
private MySingleton(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20);
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
public static synchronized MySingleton getInstance(Context context) {
if (mInstance == null) {
mInstance = new MySingleton(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}
public ImageLoader getImageLoader() {
return mImageLoader;
}
}
RequestQueue
使用singleton类执行操作的示例
kotlin:
// Get a RequestQueue
val queue = MySingleton.getInstance(this.applicationContext).requestQueue
// ...
// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest)
java:
// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
getRequestQueue();
// ...
// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);
使用volley标准的发起请求
使用Volley支持的常见请求类型:
StringRequest
。指定URL并接收原始字符串作为响应。JsonObjectRequest
和JsonArrayRequest
(两个子类JsonRequest
)。指定URL并分别获取JSON对象或数组(作为响应)。
第一个字符串是比较简单的,上面有例子,现在说说json数据作为相应的请求发起方式
请求JSON
-
JsonArrayRequest
- 请求JSONArray
在给定URL处检索 响应正文。 -
JsonObjectRequest
-JSONObject
在给定URL处检索响应主体的请求 ,允许将可选项JSONObject
作为请求主体的一部分传入。
这两个类都基于公共基类JsonRequest。
例子:
java:
String url = "http://my-json-feed";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
mTextView.setText("Response: " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle error
}
});
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);
kotlin:
val url = "http://my-json-feed"
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
Response.Listener { response ->
textView.text = "Response: %s".format(response.toString())
},
Response.ErrorListener { error ->
// TODO: Handle error
}
)
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest)
自定义请求
基于Gson
实现自定义JSON
请求。
如果响应是字符串,图像或JSON
,则可能不需要实现自定义Request
。
对于需要实现自定义请求的情况,您只需执行以下操作:
- 扩展
Request<T>
类,其中<T>
表示请求所期望的已解析响应的类型。因此,如果解析后的响应是字符串,则通过扩展创建自定义请求Request<String>
。 - 实现抽象方法,
parseNetworkResponse()
和deliverResponse()
。
parseNetworkResponse
对于Response
相应的封装的传递,使用给定类型(例如字符串,图像或JSON)解析的封装的传递。
例子:
kotlin:
override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
return try {
val json = String(
response?.data ?: ByteArray(0),
Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response))
}
// handle errors
// ...
}
java:
@Override
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));
}
// handle errors
// ...
}
注意:parseNetworkResponse()
从工作线程调用,以确保UI线程不会被阻塞。
deliverResponse
调用这个方法parseNetworkResponse()
切换到主线程,
例子:
kotlin:
override fun deliverResponse(response: T) = listener.onResponse(response)
java:
protected void deliverResponse(T response) {
listener.onResponse(response);
GsonRequest 示例
kotlin:
/**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/
class GsonRequest<T>(
url: String,
private val clazz: Class<T>,
private val headers: MutableMap<String, String>?,
private val listener: Response.Listener<T>,
errorListener: Response.ErrorListener
) : Request<T>(Method.GET, url, errorListener) {
private val gson = Gson()
override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders()
override fun deliverResponse(response: T) = listener.onResponse(response)
override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
return try {
val json = String(
response?.data ?: ByteArray(0),
Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response))
} catch (e: UnsupportedEncodingException) {
Response.error(ParseError(e))
} catch (e: JsonSyntaxException) {
Response.error(ParseError(e))
}
}
}
java:
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/
public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
@Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
@Override
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));
}
}
}