关于okhttp:Android网络请求监控与数据上报

Monitor-Network基于 ServiceLoader,监控 Okhttp网络申请,实现拦挡、解析、转发、存储、上报等性能。采纳组件化开发方式,各业务组件均可独自拆分应用,同时提供export对外调用组件,内部调用无需关怀外部实现,组件之间集成齐全解耦 Github 地址 > https://github.com/MannaYang/Monitor-Network集成组件介绍Components-stone提供根底依赖治理build.gradle.kts(可批改依赖版本)提供DataStore/Gson/RetrofitProvider对外调用治理类(可替换)提供NetworkInterceptor全局拦截器(Okhttp Interceptor)(如需替换,需全局解决接口注解)提供Application生命周期散发治理,基于@AutoService注解生成并解耦(如需替换,需全局解决接口注解)Components-http-export该组件为对外集成组件,可间接依赖 HttpDataResult.kt提供对外获取Interceptor拦截器解析实体modelHttpInterceptorListener.kt提供对外转发实体model数据接口,可供下层业务获取网络申请数据并自定义解决,参考app组件模块[HttpInterceptor.kt]Components-http该组件是http-export外部业务实现组件,解决网络申请白名单、解析、转发等业务操作,不可间接依赖,倡议下层business业务集成 HttpInterceptor.kt拦挡原始okhttp网络申请,并组装实体模型HttpResult.ktHttpResultParser.kt解析原始申请header、body、formBody并组合[HttpDataResult]模型DispatchProvider.kt通过异步协程与ServiceLoader转发http解决实现的数据模型Components-room-export该组件为对外集成组件,可间接依赖 HttpDataEntity.kt数据库表映射关联实体模型,供内部组合数据并存储数据源,参考app组件HttpInterceptor.ktHttpDataManage.kt数据库对外操作接口,提供增删改查及自定义查问sql性能QueryFilter.kt自定义查问过滤条件,参考app组件MainActivity.ktComponents-room该组件是room-export外部业务实现组件,提供room database、table、dao实现 HttpDataBaseLifecycle.kt生命周期内初始化room相干组件,获取dao操作实例HttpDataDaoProvider.kt实体模型与Table表映射关系关联HttpDataManage.kt数据库增删改查具体实现,关联room-export组件中[HttpDataManage.kt]Components-report-export该组件为对外集成组件,可间接依赖 WorkReport.kt对外开放业务查问过滤实体、业务上报平台辨别,关联WorkManager-InputData参数Components-report该组件是report-export外部业务实现组件,提供WorkManager后盾上报性能 dingtalk钉钉工作空间及鉴权服务与数据上报(仅作参考),具体实现可换为实际上报业务平台markdown格式化上报报文(仅作参考),具体按理论业务要求解决work解决数据上报及冗余数据处理(仅作参考),具体按理论业务要求解决Components-app下层业务集成组件壳,蕴含所有export及外部实现组件,组装额定公共参数(用户信息、设施信息、利用信息) kapt("com.google.auto.service:auto-service:1.1.1")api("com.google.auto.service:auto-service-annotations:1.1.1")implementation(project(":stone"))implementation(project(":http"))implementation(project(":http-export"))implementation(project(":room"))implementation(project(":room-export"))implementation(project(":report"))implementation(project(":report-export"))具体调用办法在[MainActivity.kt]与[HttpInterceptor.kt],利用内BaseApplication应替换为我的项目理论应用类 应用阐明我的项目中为了演示成果采纳钉钉上报,因而须要申请企业应用AppKey/SecretKey/AgentId/进行鉴权以,通过以上key信息获取UserIds实现音讯推送告诉,理论利用中举荐上报到日志中台或相干日志平台,作为全链路监控的一环 钉钉开放平台 > https://open.dingtalk.com/ gradle.properties需配置以下内容,关联影响鉴权办法地位 : DingRepository.kt # Ding key configdingAppKey=dingSecretKey=dingAgentId=dingUsersId=理论调用代码示例 DingProvider.ktsuspend fun reportWorkSpace(title: String, content: String, success: (Boolean) -> Unit) { ... ... ...}可自定义DataServerProvider.kt ,解决理论数据上报平台suspend fun reportDataServer(title: String, content: String, success: (Boolean) -> Unit) { //do something}suspend fun reportDataOther(title: String, content: String, success: (Boolean) -> Unit) { //do something}演示成果截图 screenshot ...

September 25, 2023 · 2 min · jiezi

关于okhttp:深入浅出-OkHttp-源码解析及应用实践

作者:vivo 互联网服务器团队- Tie QinruiOkHttp 在 Java 和 Android 世界中被宽泛应用,深刻学习源代码有助于把握软件个性和进步编程程度。 本文首先从源代码动手简要剖析了一个申请发动过程中的外围代码,接着通过流程图和架构图概括地介绍了OkHttp的整体构造,重点剖析了拦截器的责任链模式设计,最初列举了OkHttp拦截器在我的项目中的理论利用。 一、背景介绍在生产实践中,经常会遇到这样的场景:须要针对某一类 Http 申请做对立的解决,例如在 Header 里增加申请参数或者批改申请响应等等。这类问题的一种比拟优雅的解决方案是应用拦截器来对申请和响应做对立解决。 在 Android 和 Java 世界里 OkHttp 凭借其高效性和易用性被宽泛应用。作为一款优良的开源 Http 申请框架,深刻理解它的实现原理,能够学习优良软件的设计和编码教训,帮忙咱们更好到地应用它的个性,并且有助于非凡场景下的问题排查。本文尝试从源代码登程探索 OkHttp 的基本原理,并列举了一个简略的例子阐明拦截器在咱们我的项目中的理论利用。本文源代码基于 OkHttp 3.10.0。 二、OkHttp 基本原理2.1 从一个申请示例登程OkHttp 能够用来发送同步或异步的申请,异步申请与同步申请的次要区别在于异步申请会交由线程池来调度申请的执行。应用 OkHttp 发送一个同步申请的代码相当简洁,示例代码如下: 同步 GET 申请示例// 1.创立OkHttpClient客户端OkHttpClient client = new OkHttpClient();public String getSync(String url) throws IOException { OkHttpClient client = new OkHttpClient(); // 2.创立一个Request对象 Request request = new Request.Builder() .url(url) .build(); // 3.创立一个Call对象并调用execute()办法 try (Response response = client.newCall(request).execute()) { return response.body().string(); } }其中 execute() 办法是申请发动的入口,RealCall 对象的 execute() 办法的源代码如下: ...

May 18, 2023 · 3 min · jiezi

关于okhttp:由浅入深聊聊OkHttp的那些事

引言在 Android 开发的世界中,有一些组件,无论应用层技术再怎么迭代,作为根底反对,它们仍然在那里。比方当咱们提到网络库时,总会下意识想到一个名字,即 OkHttp 。 只管对于大多数开发者而言,通常状况下应用的是往往它的封装版本 Retrofit ,不过其底层仍然离不开 Okhttp 作为根底撑持。而无论是自研网络库的二次封装,还是集体应用,OkHttp 也往往都是不二之选。 故本篇将以最新视角开始,使劲一瞥 OkHttp 的设计魅力。 本文对应的 OkHttp 版本: 4.10.0 本篇定位 中高难度,将从背景到应用形式,再到设计思维与源码解析,尽可能全面、易懂。背景每一个技术都有其变迁的历史背景与个性,本大节,咱们将聊一聊 Android网络库 的迭代史,作为开篇引语,润润眼。 对于 Android网络库 的迭代历史,如下图所示: 具体停顿如下: HttpClient Android1.0 时推出。但存在诸多问题,比方内存透露,频繁的GC等。5.0后,已被弃用; HttpURLConnection Android2.2 时推出,比 HttpClient 更快更稳固,Android4.4 之后底层曾经被 Okhttp 代替; volley Google 2013年开源,基于 HttpURLConnection 的封装,具备良好的扩展性和适用性,不过对于简单申请或者大量网络申请时,性能较差。目前仍然有不少我的项目应用(通常是老代码的保护); okhttp Square 2013年开源,基于 原生Http 的底层设计,具备 疾速 、 稳固 、节俭资源 等特点。是目前诸多热门网络申请库的底层实现,比方 Retrofit、RxHttp 等; Retrofit Square 2013年开源,基于 OkHttp 的封装,目前 支流 的网络申请库。 通过注解形式配置网络申请、REST格调 api、解耦彻底、常常会搭配 Rx等 实现 框架联动; …上述的整个过程,也正是随同了 Android 开发的各个期间,如果将上述分为 5个阶段 的话,那么则为: ...

February 13, 2023 · 4 min · jiezi

关于okhttp:OkHttp请求耗时统计

目录介绍01.先发问一个问题02.EventListener回调原理03.申请开始完结监听04.dns解析开始完结监听05.连贯开始完结监听06.TLS连贯开始完结监听07.连贯绑定和开释监听08.request申请监听09.response响应监听10.如何监听统计耗时11.利用实际之案例01.先发问一个问题OkHttp如何进行各个申请环节的耗时统计呢? OkHttp 版本提供了EventListener接口,能够让调用者接管一系列网络申请过程中的事件,例如DNS解析、TSL/SSL连贯、Response接管等。通过继承此接口,调用者能够监督整个利用中网络申请次数、流量大小、耗时(比方dns解析工夫,申请工夫,响应工夫等等)状况。02.EventListener回调原理先来看一下 public abstract class EventListener { // 依照申请程序回调 public void callStart(Call call) {} // 域名解析 public void dnsStart(Call call, String domainName) {} public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {} // 开释以后Transmitter的RealConnection public void connectionReleased(Call call, Connection connection) {} public void connectionAcquired(call, result){}; // 开始连贯 public void connectStart(call, route.socketAddress(), proxy){} // 申请 public void requestHeadersStart(@NotNull Call call){} public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) {} // 响应 public void requestBodyStart(@NotNull Call call) {} public void requestBodyEnd(@NotNull Call call, long byteCount) {} // 完结 public void callEnd(Call call) {} // 失败 public void callFailed(Call call, IOException ioe) {}}03.申请开始完结监听callStart(Call call) 申请开始 ...

September 23, 2020 · 8 min · jiezi

关于okhttp:OKHttp一基础使用

OkHttp(一)1 简介 OkHttp是现今一个较为出众的网络通信库,能够代替原生的HTTPURLConnection,反对Http/SPDY,有着简略易用,拓展性好等长处。 2 HttpURLConnecti让咱们先看看未应用OkHttp要怎么实现网络申请。 在未应用框架前,咱们基本上会应用原生的HttpClient(已淘汰)和HttpURLConnection来进行发送HTTP申请。 HttpURLConnection是一种多用途、轻量级的HTTP客户端,咱们大部分的应用程序能够应用它来进行HTTP操作。接下来简略应用其来发送一个Get办法的Http申请。 留神以下操作都是在子线程下进行的 (1)获取HttpURLConnection的实例// 用URL对象来创立HttpURLConnection URL murl = new URL("https://www.baidu.com"); HttpURLConnection connection = (HttpURLConnection)murl.openConnection;// 设置参数(超时工夫,读取超时,申请办法,header等 connection.setConnectionTimeout(8888); connection.setRequestMethod("GET"); ...// POST申请还要将参数写入到输入流中(2)连贯并获取输出流,并对其解决 connection.connect(); mInputSteam = connection.getInputSteam(); // 可选 int code = mHttpURLConnection.getResponseCode(); String respose = converStreamToString(mInputStream); Log.i("baidu", "申请状态码:" + code + "\n申请后果:\n" + respose);` //依据理论状况,来判断如何解决流数据 mInputSteam.close();(3)敞开连贯 connection.disconnect();以上就是HttpURLConnection的一个基本操作。 3 OkHttp的应用3.1 Get申请 //建造者模式构建对象 OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .get() .url("https://www.4399.com") .build(); Call call = client.newCall(request); try { //1.同步申请调用的办法是call.execute(),外部采纳的是线程阻塞(始终期待直到线程返回后果)形式间接将后果返回到Response Response response = call.execute(); //2.异步申请调用的办法是call.enqueue(Callback callback),该办法须要传入一个Callback期待后果回调的接口 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.w("cozing", "申请失败"); } @Override public void onResponse(Call call, Response response) throws IOException { //在这里解决失去的Response Log.w("cozing", "申请胜利"); } });} catch (IOException e) { e.printStackTrace();} 3.2 Post申请 OkHttpClient client = new OkHttpClient(); // 将参数结构成表单对象 FormBody formBody = new FormBody.Builder() .add("username", "admin") .add("password", "admin") .build(); Request request = new Request.Builder() .post(formBody) .url("https://www.4399.com") .build(); Call call = client.newCall(request); try { Response response = call.execute(); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.w("cozing", "申请失败"); } @Override public void onResponse(Call call, Response response) throws IOException { //在这里解决失去的Response Log.w("cozing", "申请胜利"); } });} catch (IOException e) { e.printStackTrace();} 以上就是OkHttp的一个根本应用,咱们不难看出因为其对网络申请性能进行了封装,使得整体的代码变得更加的简洁明了。 ...

July 29, 2020 · 1 min · jiezi

android-使用okhttp可能引发OOM的一个点

遇到一个问题: 需要给所有的请求加签名校验以防刷接口;传入请求url及body生成一个文本串作为一个header传给服务端;已经有现成的签名检验方法String doSignature(String url, byte[] body);当前网络库基于com.squareup.okhttp3:okhttp:3.14.2. 这很简单了,当然是写一个interceptor然后将request对象的url及body传入就好.于是有: public class SignInterceptor implements Interceptor { @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { Request request = chain.request(); RequestBody body = request.body(); byte[] bodyBytes = null; if (body != null) { final Buffer buffer = new Buffer(); body.writeTo(buffer); bodyBytes = buffer.readByteArray(); } Request.Builder builder = request.newBuilder(); HttpUrl oldUrl = request.url(); final String url = oldUrl.toString(); final String signed = doSignature(url, bodyBytes)); if (!TextUtils.isEmpty(signed)) { builder.addHeader(SIGN_KEY_NAME, signed); } return chain.proceed(builder.build()); }}okhttp的ReqeustBody是一个抽象类,内容输出只有writeTo方法,将内容写入到一个BufferedSink接口实现体里,然后再将数据转成byte[]也就是内存数组.能达到目的的类只有Buffer,它实现了BufferedSink接口并能提供转成内存数组的方法readByteArray. 这貌似没啥问题呀,能造成OOM? ...

October 14, 2019 · 5 min · jiezi

RxHttp-一条链发送请求新一代Http请求神器一

简介RxHttp是基于OkHttp的二次封装,并于RxJava做到无缝衔接,一条链就能发送一个完整的请求。主要功能如下: 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式支持Json、DOM等任意数据解析方式,可自定义数据解析器支持文件下载/上传,及进度的监听,并且支持断点下载支持在Activity/Fragment的任意生命周期方法,自动关闭未完成的请求支持添加公共参数/头部信息,且可动态更改baseUrl支持请求串行和并行gradle依赖 implementation 'com.rxjava.rxhttp:rxhttp:1.0.3'//注解处理器,生成RxHttp类,即可一条链发送请求annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.0.3'//管理RxJava及生命周期,Activity/Fragment 销毁,自动关闭未完成的请求implementation 'com.rxjava.rxlife:rxlife:1.0.4'RxHttp 源码RxLife 源码 初始化//设置debug模式,此模式下有日志打印HttpSender.setDebug(boolean debug)//非必须,只能初始化一次,第二次将抛出异常HttpSender.init(OkHttpClient okHttpClient)//或者,调试模式下会有日志输出HttpSender.init(OkHttpClient okHttpClient, boolean debug)此步骤是非必须的,不初始化或者传入null即代表使用默认OkHttpClient对象。 疑问:标题不是说好的是RxHttp,这么用HttpSender做一些初始化呢?这里先卖一个关子,后面会解答 添加公共参数/头部及重新设置url相信大多数开发者在开发中,都遇到要为Http请求添加公共参数/请求头,甚至要为不同类型的请求添加不同的公共参数/请求头,为此,RxHttp为大家提供了一个静态接口回调,如下,每发起一次请求,此接口就会被回调一次,并且此回调在子线程进行(在请求执行线程回调) HttpSender.setOnParamAssembly(new Function() { @Override public Param apply(Param p) { if (p instanceof GetRequest) {//根据不同请求添加不同参数 } else if (p instanceof PostRequest) { } else if (p instanceof PutRequest) { } else if (p instanceof DeleteRequest) { } //可以通过 p.getSimpleUrl() 拿到url更改后,重新设置 //p.setUrl(""); return p.add("versionName", "1.0.0")//添加公共参数 .addHeader("deviceType", "android"); //添加公共请求头 }});然后有些请求我们不希望添加公共参数/请求头,RxHttp又改如何实现呢?很简单,发起请求前,设置不添加公共参数,如下: Param param = Param.get("http://...") //设置是否对Param对象修饰,即是否添加公共参数,默认为true .setAssemblyEnabled(false); //设为false,就不会回调上面的静态接口到这,也许你们会有疑问,Param 是什么东东,下面就为大家讲解。 ...

May 6, 2019 · 3 min · jiezi

Android 网络优化,使用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成

一、前言谈到优化,首先第一步,肯定是把一个大功能,拆分成一个个细小的环节,再单个拎出来找到可以优化的点,App 的网络优化也是如此。在 App 访问网络的时候,DNS 解析是网络请求的第一步,默认我们使用运营商的 LocalDNS 服务。有数据统计,在这一块 3G 网络下,耗时在 200~300ms,4G 网络下也需要 100ms。解析慢,并不是 LocalDNS 最大的问题,它还存在一些更为严重的问题,例如:DNS 劫持、DNS 调度不准确(缓存、转发、NAT)导致性能退化等等,这些才是网络优化最应该解决的问题。想要优化 DNS,现在最简单成熟的方案,就是使用 HTTPDNS。今天就来聊聊,DNS、HTTPDNS,以及在 Android 下,如何使用 OKHttp 来集成 HTTPDNS。二、DNS 和 HTTPDNS2.1 什么是 DNS在说到 HTTPDNS 之前,先简单了解一下什么是 DNS?在网络的世界中,每个有效的域名背后都有为其提供服务的服务器,而我们网络通信的首要条件,就是知道服务器的 IP 地址。但是记住域名(网址)肯定是比记住 IP 地址简单。如果有某种方法,可以通过域名,查到其提供服务的服务器 IP 地址,那就非常方便了。这里就需要用到 DNS 服务器以及 DNS 解析。DNS(Domain Name System),它的作用就是根据域名,查出对应的 IP 地址,它是 HTTP 协议的前提。只有将域名正确的解析成 IP 地址后,后面的 HTTP 流程才可以继续进行下去。DNS 服务器的要求,一定是高可用、高并发和分布式的服务器。它被分为多个层次结构。根 DNS 服务器:返回顶级域 DNS 服务器的 IP 地址。顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址。权威 DNS 服务器:返回相应主机的 IP 地址。这三类 DNS 服务器,类似一种树状的结构,分级存在。当开始 DNS 解析的时候,如果 LocalDNS 没有缓存,那就会向 LocalDNS 服务器请求(通常就是运营商),如果还是没有,就会一级一级的,从根域名查对应的顶级域名,再从顶级域名查权威域名服务器,最后通过权威域名服务器,获取具体域名对应的 IP 地址。DNS 在提供域名和 IP 地址映射的过程中,其实提供了很多基于域名的功能,例如服务器的负载均衡,但是它也带来了一些问题。2.2 DNS 的问题DNS 的细节还有很多,本文就不展开细说了,其问题总结来说就是几点。1. 不稳定DNS 劫持或者故障,导致服务不可用。2. 不准确LocalDNS 调度,并不一定是就近原则,某些小运营商没有 DNS 服务器,直接调用其他运营商的 DNS 服务器,最终直接跨网传输。例如:用户侧是移动运营商,调度到了电信的 IP,造成访问慢,甚至访问受限等问题。3. 不及时运营商可能会修改 DNS 的 TTL(Time-To-Live,DNS 缓存时间),导致 DNS 的修改,延迟生效。还有运营商为了保证网内用户的访问质量,同时减少跨网结算,运营商会在网内搭建内容缓存服务器,通过把域名强行指向内容缓存服务器的地址,来实现本地本网流量完全留在本地的目的。对此不同运营商甚至实现都不一致,这对我们来说就是个黑匣子。正是因为 DNS 存在种种问题,所以牵出了 HTTPDNS。2.3 HTTPDNS 的解决方案DNS 不仅支持 UDP,它还支持 TCP,但是大部分标准的 DNS 都是基于 UDP 与 DNS 服务器的 53 端口进行交互。HTTPDNS 则不同,顾名思义它是利用 HTTP 协议与 DNS 服务器的 80 端口进行交互。不走传统的 DNS 解析,从而绕过运营商的 LocalDNS 服务器,有效的防止了域名劫持,提高域名解析的效率。这就相当于,每家各自基于 HTTP 协议,自己实现了一套域名解析,自己去维护了一份域名与 IP 的地址簿,而不是使用同一的地址簿(DNS服务器)。据说微信有自己部署的 NETDNS,而各大云服务商,阿里云和腾讯云也提供了自己的 HTTPDNS 服务,对于我们普通开发者,只需要付出少量的费用,在手机端嵌入支持 HTTPDNS 的客户端 SDK,即可使用。三、 OKHttp 接入 HTTPDNS既然了解了 HTTPDNS 的重要性,接下来看看如何在 OkHttp 中,集成 HTTPDNS。OkHttp 是一个处理网络请求的开源项目,是 Android 端最火热的轻量级网络框架。在 OkHttp 中,默认是使用系统的 DNS 服务 InetAddress 进行域名解析。InetAddress ip2= InetAddress.getByName(“www.cxmydev.com”);System.out.println(ip2.getHostAddress());System.out.println(ip2.getHostName());而想在 OkHttp 中使用 HTTPDNS,有两种方式。通过拦截器,在发送请求之前,将域名替换为 IP 地址。通过 OkHttp 提供的 .dns() 接口,配置 HTTPDNS。对这两种方法来说,当然是推荐使用标准 API 来实现了。拦截器的方式,也建议有所了解,实现很简单,但是有坑。3.1 拦截器接入方式1. 拦截器接入拦截器是 OkHttp 中,非常强大的一种机制,它可以在请求和响应之间,做一些我们的定制操作。在 OkHttp 中,可以通过实现 Interceptor 接口,来定制一个拦截器。使用时,只需要在 OkHttpClient.Builder 中,调用 addInterceptor() 方法来注册此拦截器即可。OkHttp 的拦截器不是本文的重点,我们还是回到拦截器去实现 HTTPDNS 的话题上,拦截器没什么好说的,直接上相关代码。class HTTPDNSInterceptor : Interceptor{ override fun intercept(chain: Interceptor.Chain): Response { val originRequest = chain.request() val httpUrl = originRequest.url() val url = httpUrl.toString() val host = httpUrl.host() val hostIP = HttpDNS.getIpByHost(host) val builder = originRequest.newBuilder() if(hostIP!=null){ builder.url(HttpDNS.getIpUrl(url,host,hostIP)) builder.header(“host”,hostIP) } val newRequest = builder.build() val newResponse = chain.proceed(newRequest) return newResponse }}在拦截器中,使用 HttpDNS 这个帮助类,通过 getIpByHost() 将 Host 转为对应的 IP。如果通过抓包工具抓包,你会发现,原本的类似 http://www.cxmydev.com/api/user 的请求,被替换为:http://220.181.57.xxx/api/user。2. 拦截器接入的坏处使用拦截器,直接绕过了 DNS 的步骤,在请求发送前,将 Host 替换为对应的 IP 地址。这种方案,在流程上很清晰,没有任何技术性的问题。但是这种方案存在一些问题,例如:HTTPS 下 IP 直连的证书问题、代理的问题、Cookie 的问题等等。其中最严重的问题是,此方案(拦截器+HTTPDNS)遇到 https 时,如果存在一台服务器支持多个域名,可能导致证书无法匹配的问题。在说到这个问题之前,就要先了解一下 HTTPS 和 SNI。HTTPS 是为了保证安全的,在发送 HTTPS 请求之前,首先要进行 SSL/TLS 握手,握手的大致流程如下:客户端发起握手请求,携带随机数、支持算法列表等参数。服务端根据请求,选择合适的算法,下发公钥证书和随机数。客户端对服务端证书,进行校验,并发送随机数信息,该信息使用公钥加密。服务端通过私钥获取随机数信息。双方根据以上交互的信息,生成 Session Ticket,用作该连接后续数据传输的加密密钥。在这个流程中,客户端需要验证服务器下发的证书。首先通过本地保存的根证书解开证书链,确认证书可信任,然后客户端还需要检查证书的 domain 域和扩展域,看看是否包含本次请求的 HOST。在这一步就出现了问题,当使用拦截器时,请求的 URL 中,HOST 会被替换成 HTTPDNS 解析出来的 IP。当服务器存在多域名和证书的情况下,服务器在建立 SSL/TLS 握手时,无法区分到底应该返回那个证书,此时的策略可能返回默认证书或者不返回,这就有可能导致客户端在证书验证 domain 时,出现不匹配的情况,最终导致 SSL/TLS 握手失败。这就引发出来 SNI 方案,SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的 SSL/TLS 扩展。SNI 的工作原理,在连接到服务器建立 SSL 连接之前,先发送要访问站点的域名(hostname),服务器根据这个域名返回正确的证书。现在,大部分操作系统和浏览器,都已经很好的支持 SNI 扩展。3. 拦截器 + HTTPDNS 的解决方案这个问题,其实也有解决方案,这里简单介绍一下。针对 “domain 不匹配” 的问题,可以通过 hook 证书验证过程中的第二步,将 IP 直接替换成原来的域名,再执行证书验证。而 HttpURLConnect,提供了一个 HostnameVerifier 接口,实现它即可完成替换。public interface HostnameVerifier { public boolean verify(String hostname, SSLSession session);}如果使用 OkHttp,可以参考 OkHostnameVerifier (source://src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java) 的实现,进行替换。本身 OkHttp 就不建议通过拦截器去做 HTTPDNS 的支持,所以这里就不展开讨论了,这里只提出解决的思路,有兴趣可以研究研究源码。3.2 OKHttp 标准 API 接入OkHttp 其实本身已经暴露了一个 Dns 接口,默认的实现是使用系统的 InetAddress 类,发送 UDP 请求进行 DNS 解析。我们只需要实现 OkHttp 的 Dns 接口,即可获得 HTTPDNS 的支持。在我们实现的 Dns 接口实现类中,解析 DNS 的方式,换成 HTTPDNS,将解析结果返回。class HttpDns : Dns { override fun lookup(hostname: String): List<InetAddress> { val ip = HttpDnsHelper.getIpByHost(hostname) if (TextUtils.isEmpty(ip)) { //返回自己解析的地址列表 return InetAddress.getAllByName(ip).toList() } else { // 解析失败,使用系统解析 return Dns.SYSTEM.lookup(hostname) } }}使用也非常的简单,在 OkHttp.build() 时,通过 dns() 方法配置。mOkHttpClient = httpBuilder .dns(HttpDns()) .build();这样做的好处在于:还是用域名进行访问,只是底层 DNS 解析换成了 HTTPDNS,以确保解析的 IP 地址符合预期。HTTPS 下的问题也得到解决,证书依然使用域名进行校验。OkHttp 既然暴露出 dns 接口,我们就尽量使用它。四、小结时刻现在大家知道,在做 App 的网络优化的时候,第一步就是使用 HTTPDNS 优化 DNS 的步骤。所有的优化当然是以最终效果为目的,这里提两条大厂公开的数据,对腾讯的产品,在接入 HTTPDNS 后,用户平均延迟下降超过 10%,访问失败率下降超过五分之一。而百度 App 的 Feed 业务,Android 劫持率由 0.25% 降低到 0.05%。此种优化方案,非常依赖 HTTPDNS 服务器,所以建议使用 阿里云、腾讯云 这样相对稳定的云服务商。references:【1】百度App网络深度优化系列一 DNS 优化【2】SIN:https://blog.csdn.net/makenot…【3】鹅厂网事,HTTPDNS 服务详解公众号后台回复成长『成长』,将会得到我准备的学习资料,也能回复『加群』,一起学习进步;你还能回复『提问』,向我发起提问。推荐阅读:关于字符编码,你需要知道的都在这里 | 图解:HTTP 范围请求 | Java 异常处理 | 安卓防止用户关闭动画导致动画失效 | Git 找回遗失的代码 | 阿里的 Alpha 助力 App 启动速度优化 ...

March 25, 2019 · 3 min · jiezi

OkHttp3深入了解之Interceptors

OKHttp官网上面介绍:拦截器是一种强大的机制,可以监视,重写和重试调用。这里我们主要对拦截器的网络请求头和数据请求的封装。网络请求头拦截器在Android应用中,我们通常需要获取用户手机的IMEI值和其他一些常用的参数,如果把他们封装到请求头里面的话会非常的方便。既然是自定义拦截器,我们首先肯定要继承OKHttp的拦截器,把这个自定义类设置成final类型的防止其他类继承。这里需要在父类的intercept(Chain chain)方法中进行操作。在方法里面得 Request.Builder对象然后添加请求头。创建一个BaseParam 对象,对请求头的参数进行封装。public final class HeaderInterceptorTest implements Interceptor { /** * 请求头参数基础参数 / private static final String HEADER_BASE_PARAM = “baseParam”; private static String BASE_PARAM; @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); builder.addHeader(HEADER_BASE_PARAM, BASE_PARAM); return chain.proceed(builder.build()); } public static void initBaseParam(Context context) { BaseParam baseParam = new BaseParam(); String imei; String imsi; try { imei = DeviceInfoUtil.getIMEI(context); } catch (Exception e) { imei = null; } if (null == imei || imei.equals("")) { imei = DeviceInfoUtil.getDeviceID(context); imsi = DeviceInfoUtil.getDeviceID(context); } else { imei = DeviceInfoUtil.getIMEI(context); imsi = DeviceInfoUtil.getIMSI(context); } baseParam.setImei(imei); baseParam.setImsi(imsi); baseParam.setMac(DeviceInfoUtil.getWifiMAC(context)); baseParam.setVersion(DeviceInfoUtil.getVersion(context)); baseParam.setModel(DeviceInfoUtil.getModel()); baseParam.setBrand(DeviceInfoUtil.getBrand()); BASE_PARAM = new Gson().toJson(baseParam).toString(); } private static class BaseParam { /* * imei : * imsi : * mac : * version : * model : * brand : * city : */ private String imei; private String imsi; private String mac; private String version; private String model; private String brand; private String city; public String getImei() { return imei; } public void setImei(String imei) { this.imei = imei; } public String getImsi() { return imsi; } public void setImsi(String imsi) { this.imsi = imsi; } public String getMac() { return mac; } public void setMac(String mac) { this.mac = mac; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }}数据请求拦截器 在进行数据请求的时候,一般都会定义一种固定的请求格式。在这里我们直接通过拦截器,将这个固定的格式定义好,这样使用起来可以统一管理。具体的使用方法其实和上面一样。 主要注意的是先得到,请求时设置的请求体,然后让请求的数据封装到和后端设置的请求格式里面。public class HttpDataPackInterceptorTest implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Request.Builder requestBuilder = chain.request().newBuilder(); //请求数据处理 if (request.url().toString().contains(“uploads”)) { //特殊接口的参数不需要处理的 } else { //需要处理请求参数的 if (request.body() instanceof FormBody) { FormBody.Builder newFormBody = new FormBody.Builder(); //得到请求时设置的请求体 FormBody oldFormBody = (FormBody) request.body(); Buffer buffer = new Buffer(); oldFormBody.writeTo(buffer); String postParams = JsonUtils.getJsonStrFromPostParams(buffer.readString(Charset.forName(“UTF-8”))); String data = URLDecoder.decode(postParams, “UTF-8”); if (TextUtils.isEmpty(data)) { newFormBody.add(“data”, “”); } else { //将请求的数据封装 newFormBody.add(“data”, data); } requestBuilder.method(request.method(), newFormBody.build()); } } return chain.proceed(requestBuilder.build()); }}代码中调用在创建OkHttpClient 对象的时候,调用addInterceptor()方法添加俩个拦截器。private OkHttpClient client; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_okhttp); HeaderInterceptorTest.initBaseParam(this); client= new OkHttpClient() .newBuilder() .addInterceptor(new HeaderInterceptorTest()) .addInterceptor(new HttpDataPackInterceptorTest()) .connectTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS).build(); okhttpAsyncPost(); } private void okhttpAsyncPost(){ RequestBody formBody = new FormBody.Builder() .add(“page”, “1”) .add(“count”, “2”) .add(“type”,“video”) .build(); Request request = new Request.Builder() .url(“https://api.apiopen.top/getJoke") .post(formBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.e(“error”,“结果”+response.body().string()); Log.e(“error”,“方法”+response.request().toString()); Log.e(“error”,“请求头”+response.request().headers().toString()); } }); }打印的日志结果{“status”:200,“msg”:“OK”,“data”:”{"count":"2","page":"1","type":"video"}"}方法Request{method=POST, url=https://www.26uuun.com/list, tags={}}请求头baseParam: {“brand”:“Xiaomi”,“city”:"\u4e1c\u4eac",“imei”:“9fc70b16bf169075f556e6d67be9ef1a”,“imsi”:“9fc70b16bf169075f556e6d67be9ef1a”,“mac”:“02:00:00:00:00:00”,“model”:“Mi Note 2”,“version”:“1.0”}OK,OKHttp3中Interceptors配置完成,小伙伴们,可以设置自己不同需求的其他的拦截器添加到请求中。最近我会写很多关于Android常用控件的使用,里面都是一些很有用的知识,如果你感觉有用,请给我一个star,谢谢。代码实例 ...

January 1, 2019 · 3 min · jiezi

初探OkHttp3

在应用开发中肯定会用到网络请求,下面让我们一起了解OkHttp3这个网络请求框架吧。项目中引用Module的build.gradle文件中引入:dependencies { compile ‘com.squareup.okhttp3:okhttp:3.12.1’}异步Get请求OkHttpClient 是连接对象,无论是什么请求,使用OKHttp都必须要创建这个对象。Request 是请求对象的参数,里面需要放置各种请求信息。enqueue() 是使用异步请求方法。 OkHttpClient client = new OkHttpClient(); private void okhttpAsyncGet(){ Request request = new Request.Builder() .url(“https://api.apiopen.top/getJoke?page=1&count=2&type=video") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.e(“error”,response.body().string()); } }); }异步Post请求这里我们使用Post请求,和上面Get请求用的URL是一样的。不同的是用Post请求需要使用RequestBody这个对象,用add()方法,添加我们的请求参数。private void okhttpAsyncPost(){ RequestBody formBody = new FormBody.Builder() .add(“page”, “1”) .add(“count”, “2”) .add(“type”,“video”) .build(); Request request = new Request.Builder() .url(“https://api.apiopen.top/getJoke") .post(formBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.e(“error”,response.body().string()); } }); }同步请求同步请求的话,在Android开发中不是很常用,在主线程中是不能进行网络请求的所以我们这个要开一个子线程进行同步请求。 使用同步请求的是需要调用execute()方法,Response接收返回的对象。同步和异步请求只是最后一步的请求的方法不同而已。 private Runnable runnable; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_okhttp); okhttpPost(); new Thread(runnable).start(); } private void okhttpPost() { runnable = new Runnable(){ @Override public void run() { try { RequestBody formBody = new FormBody.Builder() .add(“page”, “1”) .add(“count”, “2”) .add(“type”,“video”) .build(); Request request = new Request.Builder() .url(“https://api.apiopen.top/getJoke") .post(formBody) .build(); Response response = client.newCall(request).execute(); Log.e(“error”,response.body().string()); } catch (Exception e) { Toast.makeText(OkhttpActivity.this,“异常”,Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } }; }响应超时设置当请求服务器没有反应时,这里我们可以通过OkHttpClient 对象来设置请求响应超时。OkHttpClient client = new OkHttpClient() .newBuilder() .connectTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .build();上面的URL都是可以使用测试的。Ok,这样我们就可以简单快速上手OKHttp3了,后面会继续讲解OKHttp3的一些高级用法。最近我会写很多关于Android常用控件的使用,里面都是一些很有用的知识,如果你感觉有用,请给我一个star,谢谢。代码实例 ...

January 1, 2019 · 1 min · jiezi