现在很多人都说volley已经过时了,因为现在主流的就是OKhttp和retrofit以及dragger2了,框架是主流的MVP框架,使用这些网络请求库更优良,这段时间刚好有空,想整理一下,我的理解里,没有过时不过时,没有哪个好,哪个不好,技术是用来解决实际问题的,有诸多的选择的时候,一定有一个最适合当前问题的解决方案,因为每一个工具都有其特点、优势、劣势,那些淘汰掉的是因为有更方便更优良的新生代出来了,我的项目目前使用的是volley网络框架,我先对这个网络框架进行一下总结,对于其他的网络框架还没有很多的研究,不敢多说是非,后续有了比较深入的研究再写个帖子说两句,也欢迎各位朋友指点批评。。。。
volley是google官方推出的http网络请求库。 话不多说,先来官方资料,贴出官方地址:
volley的GitHub地址 ,https://github.com/google/volleyvolley的官方使用介绍地址 https://developer.android.com/training/volley/ (此处需要科学上网才可以访问,你懂得)volley最主要的特点是: 体积小、速度快,非常适合小数据量的频繁请求; 当然不适合大型下载或流媒体操作, 然而volley也对于这种它不适合的也给出了解决办法, 使用DownloadManager替代。
特点
自动调度网络请求。多个并发网络连接。具有标准HTTP 缓存一致性的透明磁盘和内存响应缓存 。支持请求优先级。取消请求API。您可以取消单个请求,也可以设置要取消的请求块或范围。易于定制,例如,重试和退避。强大的排序,可以使用从网络异步获取的数据轻松正确填充UI。调试和跟踪工具。 怎么样是不是有的看不懂什么意思?说实话其实我也是,哥们别着急,继续看。响应回调,支持原始字符串,图像和JSON。
Volley擅长用于填充UI的RPC类型操作,例如将搜索结果页面作为结构化数据获取。 Volley不适合大型下载或流媒体操作,因为Volley在解析期间将所有响应保存在内存中。对于大型下载操作,请考虑使用类似的替代方法DownloadManager
Volley添加到项目的最简单方法是将以下依赖项添加到应用程序的build.gradle文件中: 方式1
dependencies { ... compile 'com.android.volley:volley:1.1.1' }使用implementation替换compile,因为从2018年底开始Google已经要删除这个关键字了,统一使用implementation。
方式2 1,Git通过在命令行键入以下内容来克隆存储库:
git clone https://github.com/google/volley2,将下载的源作为Android库模块导入到应用项目中。如何创建android库模块,后续再写一篇帖子说。
可以通过创建RequestQueue并传递 Request对象来使用Volley 。在RequestQueue管理诸多的网络请求子线程(网络请求属于耗时操作,是必须放在子线程的),这个队列负责网络请求子线程的网络请求操作、读取和写入到缓存中,解析响应管理工作线程等内容。Requests负责解析原始响应,Volley负责将解析后的响应分发回主线程以进行传递。
要使用Volley,必须不能忘记将android.permission.INTERNET权限添加 到应用的清单中。如果没有这个,应用将无法连接到网络。-发送一个请求使用Volley.newRequestQueue方法,向这个方法中传入Request对象,以发起网络请求,RequestQueue可以不进行设置,直接使用默认值也可以启动队列; 先说发起一个网络请求的套路,稍后再说这个发起请求必须用到的这个RequestQueue。
Volley总是在主线程上提供已解析的响应。在主线程上运行可以方便地使用接收到的数据更新填充UI控件,因为可以直接从handler响应修改UI控件,
要发送请求,只需构建一个请求并将其添加到RequestQueue使用 add()方法,就可以将这个请求添加进队列。添加请求后,它将在队列的管道中移动,获得服务,解析并传递其原始响应。 当调用add()时,Volley运行一个缓存处理线程和一个网络调度线程池。当向队列添加请求时,它会被缓存线程拾取并进行分类:如果请求可以从缓存中获得服务,则缓存响应将在缓存线程上进行解析,并且解析后的响应将在主线程上传递。如果无法从缓存中为请求提供服务,则将其置于网络队列中。第一个可用的网络线程从队列中获取请求,执行HTTP事务,解析工作线程上的响应,将响应写入缓存,并将解析的响应发送回主线程以进行传递。 注意,I / O阻塞、解析/解码等昂贵的操作是在工作线程上完成的。可以从任何线程添加请求,但响应始终在主线程上传递。 以上是volley发起网络请求的生命周期。
要取消请求,通过Request对象调用cancel()方法完成。一旦取消,Volley保证永远不会调用当前http请求线程的响应handler。可以在activity的onstop()方法中直接取消所有待处理的请求。而不用做getActivity() == null或者onSaveInstanceState()是否已经执行等检查操作后再取消待处理的请求。 要利用此行为,您通常必须跟踪所有正在进行的请求,以便能够在适当的时间取消它们。有一种更简单的方法: 可以将标记对象与每个请求相关联。然后,您可以使用此标记提供取消请求的范围。 例如,您可以将所有请求标记为Activity代表他们发出的请求,在onStop()方法中执行requestQueue.cancelAll(this)表示取消当前activity的所有待处理的请求。 同样,可以在ViewPager选项卡中使用各自的选项卡标记所有缩略图图像请求, 并在滑动时取消已经划过去的选项卡页面的待处理请求,以确保新选项卡不会被另一个选项卡的请求阻止阻塞。
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,在活动的onStop()方法中,取消所有具有此标记的请求。 kotlin:
protected fun onStop() { super.onStop() requestQueue?.cancelAll(TAG) }java:
@Override protected void onStop () { super.onStop(); if (mRequestQueue != null) { mRequestQueue.cancelAll(TAG); } }取消request后,请求的相应handler是不会被执行的,请求直接就被停止了。
上面是直接使用Volley.newRequestQueue发起一个请求,使用的是默认的RequestQueue;当然还可以创建一个RequestQueue通过设置它来提供提强求的自定义行为。 RequestQueue的创建最好使用单例模式,在APP的运行生命周期中一直保持RequestQueue对象的存活。 RequestQueue需要做两件事:一个用于执行request传输的网络请求,一个用于处理缓存线程的缓存。 Volley工具箱中DiskBasedCache提供了这些标准实现:提供带有内存索引的单文件响应缓存,BasicNetwork根据您首选的HTTP客户端提供网络传输。 BasicNetwork是Volley的默认网络实现。BasicNetwork 必须使用HTTP客户端实例化,以便应用程序连接到网络. 通常这个HTTP客户端是一个HttpURLConnection。 ###设置RequestQueque 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) // ...但是通常这个设置不用进行的,使用默认的设置就OK的,除非特殊的需要。 注意: 如果只发起一次网络请求,那么就可以使用上面的发送简单请求的方式,创建RequestQueue发起请求,当请求response返回以后或者请求出错以后调用stop()方法。使用Volley.newRequestQueue()方法可以发起一次简单的网络请求,但是更多的时候是使用単例的方式创建RequestQueue。
如果应用程序不断使用网络,那么设置一个単例RequestQueue对象,保持应用程序生命周期的过程中该对象都存活,这样的话可能效率最高。可以通过各种方式实现这一目标。推荐的方法是实现封装RequestQueue和其他Volley功能的单例类。另一种方法是子类化Application并设置 RequestQueue in Application.onCreate()。但是这种方法是 不鼓励的 ; 静态单例可以以更模块化的方式提供相同的功能。 一个关键概念是RequestQueue必须使用Application上下文实例化 ,而不是Activity上下文。这可确保在RequestQueue应用程序的生命周期内持续使用,而不是每次重新创建活动时重新创建(例如,当用户旋转设备时)。 一句话,哥们创建一次RequestQueue对象之后,整个APP就可以一直使用这个对象不需要重新开页面的时候重新创建一个新对象。
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; } }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);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)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);自定义请求,其实是因为,volley直接支持== 字符串String,图像或JSON,则可能不需要实现自定义Request。==
== ** 如果要进行自定义请求,只需要继承他们的父类,泛型中传入自定义的类型即可。**
/** * A request for retrieving a T type response body at a given URL that also optionally sends along a * JSON body in the request specified. * * @param <T> JSON type of response expected */ public abstract class JsonRequest<T> extends Request<T> {对于需要实现自定义请求的情况,您只需执行以下操作:
扩展Request<T>类,其中 <T>表示请求所期望的已解析响应的类型。因此,如果解析后的响应是字符串,则通过扩展创建自定义请求Request<String>。实现抽象方法,parseNetworkResponse() 和deliverResponse()。Response对于给定类型(例如字符串,图像或JSON), 封装解析的传递响应。以下是一个示例实现 parseNetworkResponse(): 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()将参数作为参数 NetworkResponse,其中包含byte [],HTTP状态代码和响应头作为相应response。 实现必须返回一个Response<T>,其中包含键入的响应对象和缓存元数据或错误,例如在解析失败的情况下。kotlin:
return Response.success(myDecodedObject, HttpHeaderParser.parseCacheHeaders(response))java:
return Response.success(myDecodedObject, HttpHeaderParser.parseCacheHeaders(response));Volley 调用parseNetworkResponse()方法从工作线程也就是子线程调用。这确保了昂贵的解析操作(例如将JPEG解码为Bitmap)不会阻塞UI线程。
Volley使用您返回的对象回拨主线程 parseNetworkResponse()。大多数请求在此处调用回调接口,例如: kotlin:
override fun deliverResponse(response: T) = listener.onResponse(response)java:
protected void deliverResponse(T response) { listener.onResponse(response);Gson是一个库,使用反射将Java对象转换为JSON和从JSON转换为java对象。可以定义与其对应的JSON键具有相同名称的Java对象,将Gson传递给类对象,Gson将为您填写字段。 也就是说使用Gson可以将json数据自动转换为对应的具体的实体类类型比如person类型,也可以将person转为json。 这是使用Gson进行解析的Volley请求的完整实现: 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)); } } }