美文网首页Android开发收藏夹android开发Android 轮子
Android 网络--我是怎么做的: Volley+OkHtt

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

作者: 荆全齐 | 来源:发表于2015-07-29 20:38 被阅读25977次

    Volley 已经发布很长时间了, 也已被广泛应用, 相关教程到处都是. 本文只说两个值得注意的地方.
    本文讲解部分比较少, 请参阅提供的相关链接. 完整的实现代码在 [Github dodocat/AndroidNetworkDemo] 可能看起来比这里更清晰.

    使用 OkHttp 作为传输层的实现.

    Volley 默认根据 Android 系统版本使用不同的 Http 传输协议实现.
    在 Android 2.3以下使用 ApacheHttpStack 作为传输协议, 在 3.0 及以下使用 HttpURLConnection 作为传输层协议 (感谢评论中指正的朋友).

    OkHttp 相较于其它的实现有以下的优点.

    • 支持SPDY,允许连接同一主机的所有请求分享一个socket。
    • 如果SPDY不可用,会使用连接池减少请求延迟。
    • 使用GZIP压缩下载内容,且压缩操作对用户是透明的。
    • 利用响应缓存来避免重复的网络请求。
    • 当网络出现问题的时候,OKHttp会依然有效,它将从常见的连接问题当中恢复。
    • 如果你的服务端有多个IP地址,当第一个地址连接失败时,OKHttp会尝试连接其他的地址,这对IPV4和IPV6以及寄宿在多个数据中心的服务而言,是非常有必要的。

    因此使用 OkHttp 作为替代是好的选择.

    1. 首先用 OkHttp 实现一个新的 HurlStack 用于构建 Volley 的 requestQueue.

    public class OkHttpStack extends HurlStack {

    private OkHttpClient okHttpClient;
    
    /**
     * Create a OkHttpStack with default OkHttpClient.
     */
    public OkHttpStack() {
        this(new OkHttpClient());
    }
    
    /**
     * Create a OkHttpStack with a custom OkHttpClient
     * @param okHttpClient Custom OkHttpClient, NonNull
     */
    public OkHttpStack(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
    }
    
    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
        OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient);
        return okUrlFactory.open(url);
    }
    

    }

    
    1. 然后使用 OkHttpStack 创建新的 Volley requestQueue. 
        ``` java
    requestQueue = Volley.newRequestQueue(getContext(), new OkHttpStack());
    requestQueue.start();
    

    这样就行了.

    使用 Https

    作为一个有节操的开发者应该使用 Https 来保护用户的数据, Android 开发者网站上文章[Security with HTTPS and SSL]做了详尽的阐述.

    OkHttp 自身是支持 Https 的. 参考文档 [OkHttp Https], 直接使用上面的 OkHttpStack 就可以了, 但是如果遇到服务器开发哥哥使用了自签名的证书(不要问我为什么要用自签名的), 就无法正常访问了.

    网上有很多文章给出的方案是提供一个什么事情都不做的TrustManager 跳过 SSL 的验证, 这样做很容受到攻击, Https 也就形同虚设了.

    我采用的方案是将自签名的证书打包入 APK 加入信任.

    好处:

    • 应用难以逆向, 应用不再依赖系统的 trust store, 使得 Charles 抓包等工具失效. 要分析应用 API 必须反编译 APK.
    • 不用额外购买证书, 省钱....

    缺点:

    • 证书部署灵活性降低, 一旦变更证书必须升级程序.

    实现步骤

    以最著名的自签名网站12306为例说明

    1. 导出证书

       echo | openssl s_client -connect kyfw.12306.cn:443 2>&1 |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > kyfw.12306.cn.pem
      
    2. 将证书转为 bks 格式
      下载最新的bcprov-jdk, 执行下面的命令. storepass 是导出密钥文件的密码.

    export CERTSTORE=kyfw.12306.cn.bks
    keytool -importcert -v
    -trustcacerts
    -alias 0
    -file <(openssl x509 -in kyfw.12306.cn.pem)
    -keystore $CERTSTORE -storetype BKS
    -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider
    -providerpath ./bcprov-jdk16-1.46.jar
    -storepass asdfqaz
    ```

    1. 将导出的 kyfw.bks 文件放入 res/raw 文件夹下.

    2. 创建 SelfSignSslOkHttpStack

    /**

    • A HttpStack implement witch can verify specified self-signed certification.
      */
      public class SelfSignSslOkHttpStack extends HurlStack {

      private OkHttpClient okHttpClient;

      private Map<String, SSLSocketFactory> socketFactoryMap;

      /**

      • Create a OkHttpStack with default OkHttpClient.
        */
        public SelfSignSslOkHttpStack(Map<String, SSLSocketFactory> factoryMap) {
        this(new OkHttpClient(), factoryMap);
        }

      /**

      • Create a OkHttpStack with a custom OkHttpClient
      • @param okHttpClient Custom OkHttpClient, NonNull
        */
        public SelfSignSslOkHttpStack(OkHttpClient okHttpClient, Map<String, SSLSocketFactory> factoryMap) {
        this.okHttpClient = okHttpClient;
        this.socketFactoryMap = factoryMap;
        }

      @Override
      protected HttpURLConnection createConnection(URL url) throws IOException {
      if ("https".equals(url.getProtocol()) && socketFactoryMap.containsKey(url.getHost())) {
      HttpsURLConnection connection = (HttpsURLConnection) new OkUrlFactory(okHttpClient).open(url);
      connection.setSSLSocketFactory(socketFactoryMap.get(url.getHost()));
      return connection;
      } else {
      return new OkUrlFactory(okHttpClient).open(url);
      }
      }
      }

      
      
    1. 然后用 SelfSignSslOkHttpStack 创建 Volley 的 RequestQueue.

      String[] hosts = {"kyfw.12306.cn"};
      int[] certRes = {R.raw.kyfw};
      String[] certPass = {"asdfqaz"};
      socketFactoryMap = new Hashtable<>(hosts.length);
      
      for (int i = 0; i < certRes.length; i++) {
          int res = certRes[i];
          String password = certPass[i];
          SSLSocketFactory sslSocketFactory = createSSLSocketFactory(context, res, password);
          socketFactoryMap.put(hosts[i], sslSocketFactory);
      }
      
      HurlStack stack = new SelfSignSslOkHttpStack(socketFactoryMap);
      
      requestQueue = Volley.newRequestQueue(context, stack);
      requestQueue.start();
      
    2. 我们来试一试, 用上一步穿件的 RequestQueue 替换掉原来的, 然后发请求试试.

      
          StringRequest request = new StringRequest(
                  Request.Method.GET,
                  "https://kyfw.12306.cn/otn/",
                  new Response.Listener<String>() {
                      @Override
                      public void onResponse(String response) {
                          responseContentTextView.setText(response);
                      }
                  },
                  new Response.ErrorListener() {
                      @Override
                      public void onErrorResponse(VolleyError error) {
                          responseContentTextView.setText(error.toString());
                      }
                  });
          RequestManager.getInstance(this).addRequest(request, this);
      
    
    1. done
    
    
    [Volley]:http://developer.android.com/training/volley/index.html
    [OkHttp]:http://square.github.io/okhttp/
    [Gson]:https://github.com/google/gson
    
    [Security with HTTPS and SSL]:https://developer.android.com/training/articles/security-ssl.html
    [OkHttp Https]:https://github.com/square/okhttp/wiki/HTTPS
    [Github dodocat/AndroidNetworkDemo]:https://github.com/dodocat/AndroidNetworkdemo

    相关文章

      网友评论

      • MiBoy:那公签证书的验证怎么做呢
      • 09f85f57010e:sn:
        volley使用okhttp作为传输层实现;
        什么都不做的TrustManager跨过SSL使https形同虚设
        bcprov-jdk下载连接:http://pan.baidu.com/s/1bo8Xfun
      • fe205e5a28a5:请求不到数据,说证书未找到。求楼主指点
      • fe205e5a28a5:请问createSSLSocketFactory(mCxt, res, password);方法是怎么写的
      • fe205e5a28a5:请问OkhttClient,SSLSocketFactory分别是哪个包下面的
      • zT2m8T:请问你是怎么知道kyfw.12306.cn 的 storepass是 asdfqaz
      • 271a34992389:写的很好,
        不过SSLContext.getInstance("TSL")中的TSL和SSL的区别是什么啊?
        不知道该怎么使用这个
      • LulPerer:好文谢谢分享!
      • Kaede:OkHttp3已经移除了对HttpUrlConnection的适配……
      • 63390e87c162:同求createSSLSOcketFactory
        荆全齐:@龙出滔尖 文章的第一段 github 的 demo 里面有完整的代码
      • 8cfe5f2585cb:createSSLSocketFactory
        这个方法在哪啊
        荆全齐:@终极小强 文章的第一段 github 的 demo 里面有完整的代码
      • ce52e67fec26:java.lang.NoSuchFieldError: com.squareup.okhttp.internal.http.HttpMethod.METHODS
        at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.setRequestMethod(HttpURLConnectionImpl.java:550)
        at com.android.volley.toolbox.HurlStack.setConnectionParametersForRequest(HurlStack.java:250)
        at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:111)
        at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:97)
        at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:112)

        问下,楼主,我使用okhttp2.6.0 ,okhttp-urlconnection.jar2.0.0 报这个异常是什么原因啊
      • 5ff6284723c5:你好,我命令输入后发生错误是什么情况,谢谢
        C:\Users\YTR-101\Desktop\Office2013含破解工具>keytool -importcert -v -trustcacer
        ts -file "yycaishen.com.crt" -alias "ddd" -keystore "my.bks" -provider org.bounc
        ycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk15on-152.jar"
        -storetype BKS
        keytool 错误: java.lang.IllegalArgumentException
        java.lang.IllegalArgumentException
        at sun.net.www.ParseUtil.decode(ParseUtil.java:202)
        at sun.misc.URLClassPath$FileLoader.<init>(URLClassPath.java:1204)
        at sun.misc.URLClassPath$3.run(URLClassPath.java:525)
        at sun.misc.URLClassPath$3.run(URLClassPath.java:520)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.misc.URLClassPath.getLoader(URLClassPath.java:519)
        at sun.misc.URLClassPath.getLoader(URLClassPath.java:492)
        at sun.misc.URLClassPath.getNextLoader(URLClassPath.java:457)
        at sun.misc.URLClassPath.getResource(URLClassPath.java:211)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:365)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at sun.security.tools.keytool.Main.doCommands(Main.java:670)
        at sun.security.tools.keytool.Main.run(Main.java:340)
        at sun.security.tools.keytool.Main.main(Main.java:333)
      • 8cfe5f2585cb:OkUrlFactory
        这个类找不到了,okhttp版本 2.5.0
        8cfe5f2585cb:@曙光之辉 导入 okhttp-urlconnection 3.2.0 包
        Jianshu_xiaojin:okhttp 3.2 找不到okurlFactory怎么解决的
        8cfe5f2585cb:@终极小强 解决了~忘了导入okhttp-urlconnection包
      • e2dc6ce9367c:用DEMO跑了一个 完全成功 但是换到自己的BKS文件后 却报错 host是服务器的地址
        求教哪里有问题

        com.android.volley.NoConnectionError:
        javax.net.ssl.SSLHandshakeException:
        java.security.cert.CerPathValidatorException:Trust anchor for certification path not found.
        1282e64d3aee:@白衣漂漂 域名写错了。擦
        1282e64d3aee:@nervvvv 我也遇到同样的问题 请问解决了吗
      • e371c64445aa:请问dump.sh需要怎样才可以运行成功的?
        e371c64445aa:@荆全齐 找到了个方法:http://blog.csdn.net/arjinmc/article/details/47108061
        e371c64445aa:@荆全齐 运行后就产生了这个文件:kyfw.12306.cn.pem 而已
        荆全齐:@qq137712630 不是可以直接运行的么?
      • 江上夜泊人5728:感觉既然用okhttp没必要和volley掺和到一块
      • c20db604f09a:请问楼主用的库都是什么版本的
        这些代码,在
        okhttp 2.5
        okio 1.0
        okhttp-urlconnection 2.0
        volley 1.0.11
        就会报错
        java.lang.NoSuchFieldError: com.squareup.okhttp.internal.http.HttpMethod.METHODS

        把okhttp换成2.0版本就能行
      • kass:看了几篇博客,好些人都是这么用的。
        想问下将 volley 和 okhttp 嫁接是出于什么目的?OKhttp不也自带异步请求队列么?
        volley 的API更好用?还是什么?
        darkengine:如果已有项目已经用volley实现了,这样修改的升级代价应该是最小的。
        半岛铁盒里的猫:对OkHttp不熟悉,同问两者结合使用和单独使用OkHttp有什么区别?
      • Haogg:在 Android 3.0 以上 Volley 使用 ApacheHttpStack 作为传输协议, 在2.3 及以下使用 HttpURLConnection 作为传输层协议


        -----------------------这是很明显的错误,一切要以代码为准。不要看别人博客怎么说就怎么说。

        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.html
        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
        }
        荆全齐:@Haogg 感谢指正!
      • 7c7f58e2a6dc:好奇怎么做到的?为什么我写的时候代码格式会被毁掉
      • 微凉一季:写错了吧,2.3以上用的是urlconnection,2.3以下用的httpclient、
        微凉一季:@慕课网 这个对markdown支持的还不太完善吧。尽量使用简单的格式就好了。另外你是女的还是男的 :smiley:
        妙法莲花1234:@微凉一季 原来你也在这里。。。
        7c7f58e2a6dc:@微凉一季 为什么我写的时候代码格式会被毁掉
      • MrRock:@荆全齐 也看过很多开源项目改造volley的,只是考虑到不利于volley的升级,每次更新都要再次改造?
        荆全齐:@慕女神 我是用 markdown 编写的 有一个代码的标记.
        7c7f58e2a6dc:@荆全齐 为什么我写的时候代码格式会被毁掉
        荆全齐:@MrRock 继承重写又不用改 Volley 的源码...
      • MrRock:想请教一下,volley本身不自带multipart参数功能,okhttp貌似可以,但是这样整合后还能用到吗?我一直在思考弄一个完整功能的网络请求模块,苦于没有找到非常好的解决方案
        荆全齐:@MrRock Multipart 也只是一个数据组合的定义而已, 可以轻松通过集成重写 Volley Request 实现.
      • alighters:非常不错,有个问题请教一下啊。res/raw 跟assets下面的资源在打包生成apk的过程中,会原封不动地放进apk中,这样在反编译apk之后,不是kyfw.bks又可以被别人拿到的么?
        荆全齐:@lighters 这里指的是不能通过代理抓包的方式对 api 进行逆向分析, 想要获取应用的网络 API 设计必须逆向.
        alighters:@荆全齐 所以所说的“难以逆向”,是指反编译APK这一步么?
        荆全齐:@lighters 这个证书本身就是公钥的角色, 本来就是公开的. 你看到上面的代码, 这个证书也是通过所有人都能访问的公开方式获取到的.

      本文标题:Android 网络--我是怎么做的: Volley+OkHtt

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