美文网首页
Android 网络Volley学习

Android 网络Volley学习

作者: JustDo23 | 来源:发表于2016-12-07 20:01 被阅读165次

    引言:对于网络请求这块来说最早接触是AsyncHttpClient后来又简单接触了xUtils再后来就是OkHttp了,官方的Volley只是听说过,并没有真正使用过。

    时间:2016年12月03日00:18:35

    作者:JustDo23

    GitHub:https://github.com/mcxiaoke/android-volley

    01. 简介

    在慕课网上找了相关视频Android-Volley详解

    Volley是 Google 在2013年推出的 Android 平台的网络通信库。Volley特点,快捷,简单,网络图像,高效异步处理请求排序,缓存,多级别取消,和 Activity 生命周期联动;缺点是不适合进行文件的上传下载。

    Volley的请求同样是包含有getpost请求;同时根据返回结果的不同可以挑选使用StringRequestJsonObjectRequestJsonArrayRequest;请求回调有SuccessError等回调;全局建立一个请求队列,方便网络请求的添加取消;使用tag标签方便查找请求,并管理请求的取消,和 Activity 进行联动使用。

    建议:在这里强调一下,不论使用的是什么网络请求框,都应该自己进行一下二次封装,方便同一管理。

    02. 入门代码

    Gradle文件中导入依赖

    compile 'com.android.volley:volley:1.0.0'
    

    新建Application并实例化请求队列

    public class JustApplication extends Application {
    
      private static RequestQueue requestQueue;// 全局的请求队列
    
      @Override
      public void onCreate() {
        super.onCreate();
        requestQueue = Volley.newRequestQueue(this);// 实例化请求队列
      }
    
      public static RequestQueue getRequestQueue() {
        return requestQueue;
      }
    }
    

    在 Activity 中进行网络请求

    private void doStringGet() {
        String url = "https://www.baidu.com";// 参数:[请求方式][请求链接][成功回调][失败回调]
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
    
          @Override
          public void onResponse(String response) {
            LogUtil.e(response);// 这个是成功的回调
          }
        }, new Response.ErrorListener() {
    
          @Override
          public void onErrorResponse(VolleyError error) {
            LogUtil.e(error.toString());// 这个是失败的回调
          }
        });
        stringRequest.setTag("doVolleyGet");// 设置标签
        JustApplication.getRequestQueue().add(stringRequest);// 将请求添加进队列
    }
    

    使用POST方式进行网络请求,和GET方式不同的是需要将参数进行封装

    private void doStringPost() {
        String url = "https://www.baidu.com";// 参数:[请求方式][请求链接][成功回调][失败回调]
        StringRequest stringRequest = new StringRequest(Request.Method.POST, url, null, null) {// 重写父类的方法,返回网络请求的参数
    
          @Override
          protected Map<String, String> getParams() throws AuthFailureError {
            Map<String, String> params = new HashMap<>();
            params.put("phone", "15812345678");
            params.put("key", "abcdefghigklmn");
            return params;
          }
        };
        stringRequest.setTag("doStringPost");// 设置标签
        JustApplication.getRequestQueue().add(stringRequest);// 将请求添加进队列
    }
    

    另外使用JsonObjectRequest进行网络请求和StringRequest也有些区别,就是不用重写返回参数,而是将一个JsonObject对象的参数直接进行传递。

    private void doJsonPost() {
        String url = "https://www.baidu.com";
        Map<String, String> params = new HashMap<>();
        params.put("phone", "15812345678");
        params.put("key", "abcdefghigklmn");
        JSONObject jsonObject = new JSONObject(params);// 将 Map 转为 JsonObject 的参数
        // 参数:[请求方式][请求链接][请求参数][成功回调][失败回调]
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() {
    
          @Override
          public void onResponse(JSONObject response) {
            LogUtil.e(response.toString());// 这个是成功的回调
          }
        }, null);
        jsonObjectRequest.setTag("doJsonPost");// 设置标签
        JustApplication.getRequestQueue().add(jsonObjectRequest);// 将请求添加进队列
    }
    

    在现有代码的基础上,给界面添加一个 TextView 并在 Volley 网络请求的回调中直接将返回的数据设置给 TextView ,测试发现回调可以直接操作UI

    03. 加载网络图片

    方式一:直接请求图片 URL 并将图片进行显示

    public void getNetImage() {
        String imageUrl = "http://img4.duitang.com/uploads/item/201509/30/20150930002351_3vHWx.thumb.700_0.jpeg";
        // 参数:[图片Url][成功回调][图片最大宽度][图片最大高度][填充方式][位图配置][失败回调]
        ImageRequest imageRequest = new ImageRequest(imageUrl, new Response.Listener<Bitmap>() {
    
          @Override
          public void onResponse(Bitmap response) {
            iv_show.setImageBitmap(response);// 请求成功返回 Bitmap 方便使用
          }// [图片最大宽度][图片最大高度]给0表示原图显示,否则会进行压缩
        }, 0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.RGB_565, new Response.ErrorListener() {
    
          @Override
          public void onErrorResponse(VolleyError error) {
            iv_show.setImageResource(R.mipmap.ic_launcher);// 请求失败的回调
          }
        });
        imageRequest.setTag("getNetImage");// 设置标签
        JustApplication.getRequestQueue().add(imageRequest);// 添加进队列
    }
    

    方式二:使用 ImageLoader 配合 ImageCache 及 ImageListener 进行图片显示

    public void getNetImage() {
        String imageUrl = "http://img4.duitang.com/uploads/item/201509/30/20150930002351_3vHWx.thumb.700_0.jpeg";
        // [获取一个 ImageLoader 对象] 参数:[请求队列][图片缓存对象]
        ImageLoader imageLoader = new ImageLoader(JustApplication.getRequestQueue(), new BitmapCache());
        // [获取一个 ImageListener 对象] 参数:[ ImageView ][默认显示图片][加载失败显示图片]
        ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(iv_show, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
        // [使用 ImageLoader 加载图片] 参数:[图片Url][图片加载监听][图片最大宽度][图片最大高度][填充方式]
        imageLoader.get(imageUrl, imageListener, 0, 0, ImageView.ScaleType.CENTER_CROP);
    }
    

    进行图片缓存的类

    /**
     * 图片缓存
     *
     * @author JustDo23
     */
    public class BitmapCache implements ImageLoader.ImageCache {
    
      private LruCache<String, Bitmap> cache;// 缓存图片
      private int maxSize = 10 * 1024 * 1024;// 最大10MB
    
      /**
       * 构造方法中实例化
       */
      public BitmapCache() {
        cache = new LruCache<String, Bitmap>(maxSize) {
          @Override
          protected int sizeOf(String key, Bitmap value) {
            return value.getRowBytes() * value.getHeight();
          }
        };
      }
    
      /**
       * 获取图片
       *
       * @param url 图片 URL
       * @return 图片 Bitmap
       */
      @Override
      public Bitmap getBitmap(String url) {
        return cache.get(url);
      }
    
      /**
       * 缓存图片
       *
       * @param url 图片 URL
       * @return 图片 Bitmap
       */
      @Override
      public void putBitmap(String url, Bitmap bitmap) {
        cache.put(url, bitmap);
      }
    }
    

    方式二:使用 Volley 内部封装的显示网络图片控件 NetworkImageView

    niv_show = (NetworkImageView) findViewById(R.id.niv_show);
    
    public void getNetImage() {
        String imageUrl = "http://img4.duitang.com/uploads/item/201509/30/20150930002351_3vHWx.thumb.700_0.jpeg";
        // [获取一个 ImageLoader 对象] 参数:[请求队列][图片缓存对象]
        ImageLoader imageLoader = new ImageLoader(JustApplication.getRequestQueue(), new BitmapCache());
        // [设置默认显示图片]
        niv_show.setDefaultImageResId(R.mipmap.ic_launcher);
        // [设置加载失败显示图片]
        niv_show.setErrorImageResId(R.mipmap.ic_launcher);
        // [设置 URL 和 ImageLoader]
        niv_show.setImageUrl(imageUrl, imageLoader);
    }
    

    04. 自定义 Request

    Android Volley完全解析(三),定制自己的Request

    当 Volley 本身提供的请求不够用的时候,可以参考 Volley 中请求的实现方式来进行自定义。其中Request<T>带泛型的请求基类。看下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));
        }
    }
    

    再此基础上,编写自定义的 Request

    /**
     * 带有 XML 解析的网络请求
     *
     * @author JustDo23
     */
    public class XMLRequest extends Request<XmlPullParser> {
    
      private final Response.Listener<XmlPullParser> mListener;// 网络请求成功的回调
    
      /**
       * [必须有一个][构造方法]
       *
       * @param method        请求方式
       * @param url           请求的网络地址
       * @param listener      成功的回调
       * @param errorListener 失败的回调
       */
      public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
      }
    
      /**
       * 默认的 GET 请求
       */
      public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
      }
    
      /**
       * [必须重写][网络请求返回数据]
       *
       * @param response 网络请求结果
       * @return 处理之后的数据
       */
      @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 (UnsupportedEncodingException e) {
          return Response.error(new ParseError(e));
        } catch (XmlPullParserException e) {
          return Response.error(new ParseError(e));
        }
      }
    
      /**
       * [必须重写][将parseNetworkResponse方法返回的数据返回给主线程]
       *
       * @param response
       */
      @Override
      protected void deliverResponse(XmlPullParser response) {
        mListener.onResponse(response);
      }
    
    }
    

    再利用 Gson 进行封装

    /**
     * 带有 Gson 解析的网络请求
     *
     * @author JustDo23
     */
    public class GsonRequest<T> extends Request<T> {
    
      private final Response.Listener<T> mListener;// 网络请求成功的回调
      private Gson mGson;// Gson 对象
      private Class<T> mClass;// 解析后 JavaBean
    
      public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mGson = new Gson();
        mClass = clazz;
        mListener = listener;
      }
    
      public GsonRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
        this(Method.GET, url, clazz, listener, errorListener);
      }
    
      @Override
      protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {// 先获取网络请求返回的Json字符串,再利用Gson将字符串转成对象
          String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
          return Response.success(mGson.fromJson(jsonString, mClass), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
          return Response.error(new ParseError(e));
        }
      }
    
      @Override
      protected void deliverResponse(T response) {
        mListener.onResponse(response);
      }
    
    }
    

    05. 再度深入

    Android Volley完全解析(四),带你从源码的角度理解Volley

    全局维护一个队列,根据手机版本的不同使用不用的HttpStack当然也可以扩展到使用OKHttp,首次初始化5个网络请求线程,先初始化一个带缓存的网络请求线程,再初始化四个不带缓存的网络请求线程。默认情况下,每条网络请求都是可以进行缓存的。获取到缓存之后,还会判断缓存是否已经过期,过期则重新进行网络请求否则直接使用缓存。

    06. Https

    Android 网络--我是怎么做的: Volley+OkHttp+Https

    利用 Volley 实现 Https 在网上大概就几种方式:直接信任所有,跳过验证,拓展到 OKHttp 添加证书等等。这篇文章是在网络上找的比较好的一篇。

    使用自签名的证书进行操作,就会遇到这些比较复杂麻烦的问题,例如访问12306网站。一般只要是使用CA证书认证的网站,默认都是信任的,直接就能进行访问的,比如https:www.baidu.com在 Volley 中同样可以直接访问不要考虑认证的问题。由于iOS年底提交项目必须使用https所以接口面临证书之类的问题,宝宝真是好害怕,一切都准备就绪后,不料买了个CA 证书,结果问题简单化。

    证书操作

    01. 将 CER 证书转成 BKS

    参考:http://blog.csdn.net/u010314594/article/details/50765534

    工具: [ JCE Provider ] bcprov-jdk15on-155.jar http://www.bouncycastle.org/latest_releases.html 在网站下载到最新版。

    命令:下载之后需要使用命令行进行操作,这个命令比较多,而且不能有回车

    keytool -importcert -v -trustcacerts -alias 位置1
    -file 位置2
    -keystore 位置3 -storetype BKS
    -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider
    -providerpath 位置4
    -storepass 位置5
    

    解释:

    位置1:是个随便取的别名,供keytool方便管理
    位置2:cer或crt证书的全地址 
    位置3:生成后bks文件的位置,建议写全地址 
    位置4:上面下载JCE Provider包的位置 
    位置5:生成后证书的密码,自己设置,需要在项目中使用,用于确保KeyStore文件本身安全
    

    命令执行之后,如果失败会有报错提示,使用最新版本基本没有问题。命令执行成功,最后会有中文[是|否]的选择,输入[是]即可完成全部过程。命名要符号 Java 命名规范。

    // 个人操作命令
    keytool -importcert -v -trustcacerts -alias test12306 -file /Users/just/Desktop/test12306/srca.cer -keystore /Users/just/Desktop/test12306/test12306.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /Users/just/Desktop/test12306/bcprov-jdk15on-155.jar  -storepass test12306
    

    02. 提取 CER 证书字符串

    命令:

    keytool -printcert -rfc -file xxx.cer
    

    命令行操作,证书文件路径可全路径,可在证书所在目录执行。

    Demo 地址

    下载

    相关文章

      网友评论

          本文标题:Android 网络Volley学习

          本文链接:https://www.haomeiwen.com/subject/wvmlmttx.html