咱们平时在开发Android应用的时候不可避免地都须要用到网络技术,而多数状况下应用程序都会使用HTTP协议来发送和接收网络数据。Android系统中主要提供了两种方式来进行HTTP通讯,HttpURLConnection和HttpClient,几乎在任何项目的代码中咱们都能看到这两个类的身影,使用率很是高。前端
不过HttpURLConnection和HttpClient的用法仍是稍微有些复杂的,若是不进行适当封装的话,很容易就会写出很多重复代码。因而乎,一些Android网络通讯框架也就应运而生,好比说AsyncHttpClient,它把HTTP全部的通讯细节所有封装在了内部,咱们只须要简单调用几行代码就能够完成通讯操做了。再好比Universal-Image-Loader,它使得在界面上显示网络图片的操做变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都作好了。
java
Android开发团队也是意识到了有必要将HTTP的通讯操做再进行简单化,因而在2013年Google I/O大会上推出了一个新的网络通讯框架——Volley。Volley但是说是把AsyncHttpClient和Universal-Image-Loader的优势集于了一身,既能够像AsyncHttpClient同样很是简单地进行HTTP通讯,也能够像Universal-Image-Loader同样轻松加载网络上的图片。除了简单易用以外,Volley在性能方面也进行了大幅度的调整,android
Volley的设计目标就是很是适合去进行数据量不大,但通讯频繁的网络操做,而对于大数据量的网络操做,好比说下载文件等,Volley的表现就会很是糟糕。json
应用场景:数据量不大,但网络通讯频繁的,处理高并发的小请求就很是适合使用Volley。数组
使用步骤缓存
对于网络通讯须要在AndroidManifest.xml添加网络权限:<uses-permission android:name="android.permission.INTERNET"/> 服务器
StringRequest的用法网络
主要就是进行了如下三步操做:并发
第一步:建立一个RequestQueue对象app
RequestQueue queue = Volley.newRequestQueue(Context);
第二步:建立一个StringRequest对象
String url = "http://www.baidu.com"; //封装一个字符串请求 StringRequest request = new StringRequest(Request.Method.GET, url, new Response .Listener<String>() { @Override public void onResponse(String response) { //请求成功 System.out.println("请求结果:" + response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { //请求失败 error.printStackTrace(); } });
第三步:将StringRequest对象添加到RequestQueue里面
queue.add(request);
这样就实现了最基本的HTTP发送与响应的功能
实现须要提交参数给服务器功能-->只须要在StringRequest的匿名类中重写getParams()方法,在这里设置POST参数就便可
实现须要提交请求头参数给服务器功能-->只须要在StringRequest的匿名类中重写getHeaders()方法,在这里设置POST参数就便可
StringRequest request = new StringRequest(Request.Method.GET, url, listener, errorListener) { @Override protected Map<String, String> getParams() throws AuthFailureError { HashMap<String, String> map = new HashMap<>(); map.put("name", "xxx"); map.put("age", 18 + ""); return map; } @Override public Map<String, String> getHeaders() throws AuthFailureError { HashMap<String, String> map = new HashMap<>(); try { String userAgent = Build.MODEL + "/" + Build.VERSION.RELEASE + " Android" + "/" + Build.VERSION.SDK + " Heima/" + getPackageManager() .getPackageInfo(getPackageName(), 0).versionName; System.out.println("user agent:" + userAgent); map.put("User-Agent", userAgent); map.put("IMEI", "8348320940480389042380"); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return map; } };
JsonRequest的用法
相似于StringRequest,JsonRequest也是继承自Request类的,不过因为JsonRequest是一个抽象类,所以咱们没法直接建立它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。
RequestQueue queue = Volley.newRequestQueue(this); String url = "http://10.0.3.2:8080/update.json"; //参3: JsonObject, 给服务器提交的参数对象 //name=xxx&age=18 //{name:"xxxx",age:18} //[] JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { //此方法在找不到字段时不会抛异常 String des = response.optString("des"); System.out.println("json解析结果:" + des2); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { error.printStackTrace(); } }); request.setTag(TAG); queue.add(request);
RequestQueue queue = Volley.newRequestQueue(this); String url = "http://10.0.3.2:8080/array.json"; JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, url, null, new Response.Listener<JSONArray>() { @Override public void onResponse(JSONArray response) { System.out.println("JsonArray:" + response.length()); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { error.printStackTrace(); } }); request.setTag(TAG); queue.add(request);
ImageRequest的用法
RequestQueue queue = Volley.newRequestQueue(this); String url = "http://img4.duitang.com/uploads/item/201403/08/20140308185645_f2hy4.thumb" + ".600_0.jpeg"; //参3,4: 图片最大宽高; 参5:缩放模式 // 参6:图片格式 ARGB_8888: 4个字节; ARGB_4444: 2字节; RGB_565: 没有透明度信息 2字节; // ALPHA_8: 灰度图片, 以后透明度值,1个字节 ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { ivImage.setImageBitmap(response); } }, 200, 200, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, new Response .ErrorListener() { @Override public void onErrorResponse(VolleyError error) { error.printStackTrace(); } }); queue.add(request);
能够看到,ImageRequest的构造函数接收六个参数,
第一个参数就是图片的URL地址;
第二个参数是图片请求成功的回调,这里咱们能够把返回的Bitmap参数设置到ImageView中。
第三第四个参数分别用于指定容许图片最大的宽度和高度,若是指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示无论图片有多大,都不会进行压缩。
第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量均可以在这里使用,其中ARGB_8888能够展现最好的颜色属性,每一个图片像素占据4个字节的大小,而RGB_565则表示每一个图片像素占据2个字节大小。
第六个参数是图片请求失败的回调,这里咱们能够当请求失败时在ImageView中设置显示一张默认图片。
ImageLoader的用法
实际上,Volley在请求网络图片方面能够作到的还远远不止这些,而ImageLoader就是一个很好的例子。ImageLoader也能够用于加载网络上的图片,而且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,由于它不只能够帮咱们对图片进行缓存,还能够过滤掉重复的连接,避免重复发送请求。
因为ImageLoader已经不是继承自Request的了,因此它的用法也和以前的用法有所不一样,总结起来大体能够分为如下几步:
1. 建立一个RequestQueue对象。
RequestQueue queue = Volley.newRequestQueue(this);
2. 建立一个ImageLoader对象。
ImageLoader imageLoader = new ImageLoader(queue, new ImageLoader.ImageCache() { @Override public void putBitmap(String url, Bitmap bitmap) { } @Override public Bitmap getBitmap(String url) { return null; } });
能够看到,ImageLoader的构造函数接收两个参数,
第一个参数就是RequestQueue对象,
第二个参数是一个ImageCache对象,这里先new出一个空的ImageCache的实现便可。
3. 获取一个ImageListener对象。
ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView,R.drawable.default_image, R.drawable.failed_image);
咱们经过调用ImageLoader的getImageListener()方法可以获取到一个ImageListener对象,getImageListener()方法接收三个参数,第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程当中显示的图片,第三个参数指定加载图片失败的状况下显示的图片。
4. 调用ImageLoader的get()方法加载网络上的图片。
String url = "http://img5.duitang.com/uploads/item/201508/05/20150805165717_NRTfW.thumb" + ".700_0.jpeg";imageLoader.get(url,listener));
5. 自定义图片缓存
虽然如今咱们已经掌握了ImageLoader的用法,可是刚才的ImageLoader的优势却尚未使用到。为何呢?由于这里建立的ImageCache对象是一个空的实现,彻底没能起到图片缓存的做用。其实写一个ImageCache也很是简单,可是若是想要写一个性能很是好的ImageCache,最好就要借助Android提供的LruCache功能了
import android.graphics.Bitmap; import android.util.LruCache; import com.android.volley.toolbox.ImageLoader; /** * 自定义内存缓存 */ public class MyImageCache implements ImageLoader.ImageCache { private LruCache<String, Bitmap> mCache; public MyImageCache() { mCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory() / 8)) { @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; } @Override public Bitmap getBitmap(String url) { return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } }
能够看到,这里咱们将缓存图片的大小设置为最大内存的八分之一,也能够写固定值好比10M。接着修改建立ImageLoader实例的代码,第二个参数传入BitmapCache的实例,以下所示:
ImageLoader imageLoader = new ImageLoader(queue, new MyImageCache());
NetWorkImageLoader的用法
除了以上两种方式以外,Volley还提供了第三种方式来加载网络图片,即便用NetworkImageView。不一样于以上两种方式,NetworkImageView是一个自定义控制,它是继承自ImageView的,具有ImageView控件的全部功能,而且在原生的基础之上加入了加载网络图片的功能。NetworkImageView控件的用法要比前两种方式更加简单,大体能够分为如下五步:
1. 建立一个RequestQueue对象
RequestQueue queue = Volley.newRequestQueue(this);
2. 建立一个ImageLoader对象
ImageLoader imageLoader = new ImageLoader(queue, new MyImageCache());
3. 在布局文件中添加一个NetworkImageView控件。
<com.android.volley.toolbox.NetworkImageView android:id="@+id/networkImageView" android:layout_width="200dp" android:layout_height="200dp" android:layout_centerHorizontal="true"/>
4. 在代码中获取该控件的实例
NetworkImageView netImage = (NetworkImageView) findViewById(R.id.networkImageView);
5. 设置要加载的图片地址
netImage.setDefaultImageResId(R.mipmap.ic_launcher); netImage.setErrorImageResId(R.mipmap.ic_launcher); netImage.setImageUrl(url, imageLoader);
第一个参数用于指定图片的URL地址,第二个参数则是前面建立好的ImageLoader对象。
说明:
NetworkImageView并不须要提供任何设置最大宽高的方法也可以对加载的图片进行压缩。这是因为NetworkImageView是一个控件,在加载图片的时候它会自动获取自身的宽高,而后对比网络图片的宽度,再决定是否须要对图片进行压缩。也就是说,压缩过程是在内部彻底自动化的,并不须要咱们关心,NetworkImageView会始终呈现给咱们一张大小刚恰好的网络图片,不会多占用任何一点内存,这也是NetworkImageView最简单好用的一点吧。
固然了,若是你不想对图片进行压缩的话,其实也很简单,只须要在布局文件中把NetworkImageView的layout_width和layout_height都设置成wrap_content就能够了,这样NetworkImageView就会将该图片的原始大小展现出来,不会进行任何压缩。
自定义GsonRequest请求
JsonRequest的数据解析是利用Android自己自带的JSONObject和JSONArray来实现的,配合使用JSONObject和JSONArray就能够解析出任意格式的JSON数据。不过仍是以为很麻烦,还有不少方法可让JSON数据解析变得更加简单,好比说GSON。但是,Volley中默认并不支持使用自家的GSON来解析数据,须要自定义一个GsonRequest。
第一步:加入Gson依赖库
第二步:定义一个GsonRequest继承自Request
import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.toolbox.HttpHeaderParser; import com.google.gson.Gson; import java.io.UnsupportedEncodingException; /** * <p> * 自定义Gson的请求 */ public class GsonRequest<T> extends Request<T> { private final Response.Listener<T> mListener; Class<T> clazz; public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); this.clazz = clazz; mListener = listener; } //解析网络返回结果 @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { String parsed; try { //参2:指的是字符编码 UTF-8,GBK...., 默认是ISO-8859-1 //此编码是经过响应头取出来的:HttpHeaderParser.parseCharset(response.headers) parsed = new String(response.data, "UTF-8"); } catch (UnsupportedEncodingException e) { //编码解析失败 parsed = new String(response.data); } //使用Gson解析json Gson gson = new Gson(); T t = gson.fromJson(parsed, clazz); //返回解析的结果 return Response.success(t, HttpHeaderParser.parseCacheHeaders(response)); } //解析成功以后的回调, 经过此方法能够将解析结果回传给前端页面 @Override protected void deliverResponse(T response) { mListener.onResponse(response); } }
能够看到,GsonRequest是继承自Request类的,而且一样提供了两个构造函数。在parseNetworkResponse()方法中,先是将服务器响应的数据解析出来,而后经过调用Gson的fromJson方法将数据组装成对象。在deliverResponse方法中仍然是将最终的数据进行回调。
第三步:经过服务的数据建立bean类
public class UpdateInfo { // { // description: "发现最新版本,赶忙下载哦!", // } public String description; }
第四步:请求的使用:
RequestQueue queue = Volley.newRequestQueue(this); String url = "http://10.0.3.2:8080/update.json"; GsonRequest request = new GsonRequest(Request.Method.GET, url, UpdateInfo.class, new Response.Listener<UpdateInfo>() { @Override public void onResponse(UpdateInfo response) { System.out.println("updateInfo:" + response.description); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { error.printStackTrace(); } }); queue.add(request);
自定义XmlRequest请求
import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.toolbox.HttpHeaderParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserFactory; import java.io.StringReader; class XMLRequest extends Request<XmlPullParser> { private final Response.Listener<XmlPullParser> mListener; public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; } public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.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)); } catch (Exception e) { return Response.error(new ParseError(e)); } } @Override protected void deliverResponse(XmlPullParser response) { mListener.onResponse(response); } }
能够看到,其实并无什么太多的逻辑,基本都是仿照StringRequest写下来的,XMLRequest也是继承自Request类的,只不过这里指定的泛型类是XmlPullParser,说明咱们准备使用Pull解析的方式来解析XML。在parseNetworkResponse()方法中,先是将服务器响应的数据解析成一个字符串,而后设置到XmlPullParser对象中,在deliverResponse()方法中则是将XmlPullParser对象进行回调。
使用:
与StringRequest使用相似,不一样之处在于返回的是XmlPullParser ,须要对onResponse()方法中解析响应的XML数据便可
为了内存优化和发挥volley优点.通常须要写一个本身的工具类,实现以下:
import android.content.Context; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; public class VolleyTools { private static VolleyTools mInstance; private final RequestQueue queue; private final ImageLoader loader; private VolleyTools(Context ctx) { queue = Volley.newRequestQueue(ctx); loader = new ImageLoader(queue, new MyImageCache()); } public static VolleyTools getInstance(Context ctx) { if (mInstance == null) { synchronized (VolleyTools.class) { if (mInstance == null) { mInstance = new VolleyTools(ctx); } } } return mInstance; } public RequestQueue getQueue() { return queue; } public ImageLoader getImageLoader() { return loader; } }
根据工具类,能够对上面的方法中的使用代码进行对应的修改便可
取消请求的实现:
步骤:
第一步:给请求设置Tag或者经过过滤器取消
request.setTag(TAG);
第二步:通常在onDestroy方法中取消
VolleyTools.getInstance(this).getQueue().cancelAll(TAG); VolleyTools.getInstance(this).getQueue().cancelAll(new RequestQueue.RequestFilter() { @Override public boolean apply(Request<?> request) { //request.getUrl().equals("") return false; } });