1. 背景 在通过数据分析的过程中,发现我们在统计上无法统计到404的场景,透过数据去查问题,由于我们App使用OkHttp、Cronet的混合,通过动态调度来进行切换,这里主要是记录使用cronet遇到的问题。
2. Cronet使用模型 Android Cronet是异步网络库(Java封装层),请求是异步,结果通过回调的方式;但是官方基于异步模型封装了一个同步请求的,接下来我们来介绍这2种方式。
2.1 异步方式请求Cronet 先看下大致的异步请求代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 private CronetEngine getCronetEngine (Context context) { return new CronetEngine .Builder(context) .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024 ) .enableHttp2(true ) .enableQuic(true ) .build(); } public void request () { Executor executor = Executors.newSingleThreadExecutor(); UrlRequest.Callback callback = new SimpleUrlRequestCallback (); UrlRequest.Builder builder = getCronetEngine().newUrlRequestBuilder( url, callback, executor); builder.setRequestFinishedListener(this ) .build().start(); } class SimpleUrlRequestCallback extends UrlRequest .Callback { @Override public void onRedirectReceived ( UrlRequest request, UrlResponseInfo info, String newLocationUrl) { request.followRedirect(); } @Override public void onResponseStarted (UrlRequest request, UrlResponseInfo info) { } @Override public void onReadCompleted ( UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) { } @Override public void onSucceeded (UrlRequest request, UrlResponseInfo info) { } @Override public void onFailed (UrlRequest var1, UrlResponseInfo var2, CronetException var3) { } }
以上代码就是cronet请求的样子,发起请求时,需要提供一个callback,这个callback,但是这个callback的触发不完全是被动,需要进行某些操作才能够触发。异步请求个人在实践过程中发现一个问题,这个问题就是在请求响应回来时,我们会对code进行判断,判断是否是http层成功(code==200),如果不是http层成功,比如4xx、5xx,那么我们就会回调一个error给上层的业务。
如下时序图:
有如下的说明:
时序图中,onResponseStarted方法是已经响应处理的地方,如果这个时候判断code非成功(成功是200=<code<300),直接return,cronet底层的metric监控将不会回调
只有进行数据的读取操作,才会调用onReadCompleted、onSucceeded
连接、ssl、数据读取会产生异常
触发Metric监控的回调cronet内部产生了异常、进行了读取数据(成功、失败)、上层调用cancel
Cronet面向Android的Java层api,没有提供close接口,只有cancel
网络底层统计逻辑,面对各种各样的请求,我们不能在面向业务回调的地方进行埋点,Cronet提供了一套完整的监控回调体系,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract static class Listener { private final Executor mExecutor; public Listener (Executor executor) { if (executor == null ) { throw new IllegalStateException ("Executor must not be null" ); } else { this .mExecutor = executor; } } public abstract void onRequestFinished (RequestFinishedInfo var1) ; public Executor getExecutor () { return this .mExecutor; } }
提供了一个抽象的类,来进行数据的收集,在上面的请求中的setRequestFinishedListener
,我们可以针对每个请求加上监听回调。
所以在底层通过RequestFinishedListener来监听获取请求网络数据,需要解决异步回调处理非 200 响应code的问题,比如如下处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public void onResponseStarted (UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) { if (!isSuccessful(urlResponseInfo.getHttpStatusCode())) { handleFail(urlResponseInfo, new CronetExceptionImpl ("onResponseStarted code error " + urlResponseInfo.getHttpStatusCode(), new Exception ("code is " + urlResponseInfo.getHttpStatusCode()) )); return ; } urlRequest.read(ByteBuffer.allocateDirect(CAPACITY)); }
判断code是否是成功的http响应,不是的话就直接return,这样就会导致底层的监听没有回调,为啥没有回调?
能回调onResponseStarted方法,说明网络层tcp层是成功的,由于没有进行数据的读取,所以不会驱动onReadCompleted、onSucceeded的回调,所以也就没有回调Metric监听器
目前底层Metric监控的状态回调中有三种状态:
1 2 3 4 5 public static final int SUCCEEDED = 0 ; public static final int FAILED = 1 ; public static final int CANCELED = 2 ;
如果在这里判断http code来决定是否读取数据,会导致监控数据缺失,所以这处应该要调用release
或者 close 接口,但cronet 在java层并没有提供。
2.2 添加close接口 由于连接、ssl这块如果出现问题,会回调onFail,也会到底层Metric接口中,所有close只需要处理以下2种场景
http层非成功的响应码(4xx、5xx这些)
同步请求中,读超时(http code 200)
3 同步请求 同步请求是使用UrlHttpConnection的接口来封装的,也就是同步调用的异步网络请求方式。
比如下载场景的,同步接口调用的时序图: