最近,部門接到新的任務就是作httpClient,URLConnection,OKhttpClient,webview的網絡性能指標進行監控,固然這些指標最準確的方法仍是使用jni在底層建鏈與dns解析,發包等函數進行hook,再進行指標採集。但領導要求咱們在java層嘗試一下,今天主要記錄下,我對OkHttp網絡指標採集的調研結果和嘗試。java
1 OkHttpClient client = new OkHttpClient(); 2 Request request = new Request.Builder().url(url).get().build(); 3 Response response = client.newCall(request).execute();
對於httpClient咱們通嘗都是替換execute()方法,而後對httpClient的對像進行更改配置本身的攔截功能,查看源碼,發現OkHttpClient相對不一樣的是execute方法並不在OkHttpClient,而是newCall方法中new 了一個call實例。web
1 public Call newCall(Request request) { 2 return new Call(this, request); 3 }
//Call構造器中,對OkHttpClient進行了深拷備,因此對newCall以後的OkHttpClient配置都將沒法生效,因此咱們必須在newCall以前將攔截代碼進行塞入
Call(OkHttpClient client, Request originalRequest) {
// Copy the client. Otherwise changes (socket factory, redirect policy,
// etc.) may incorrectly be reflected in the request when it is
// executed.
this.client = client.copyWithDefaults();
this.originalRequest = originalRequest;
}
發現Call類並非final的,因此決定對其繼承,由於 Call的構造器是protected的,因此子類須要在相同的包名下,而上層的攔截因而這樣實現的網絡
1 public static Call newCall(OkHttpClient okHttpClient, Request paramRequest) 2 { 3 return new MyCall(okHttpClient, paramRequest, okHttpClient.newCall(paramRequest)); 4 }
MyCall類的中的public,protected方法進行重寫(execute(),enqueue(Callback paramCallback),cancel(),isCanceled()),實現使用傳入的call進行調用,重寫getResponse方法socket
1 @Override 2 Response getResponse(Request request, boolean forWebSocket) throws IOException { 3 RequestBody body = request.body(); 4 if (body != null) { 5 Request.Builder requestBuilder = request.newBuilder(); 6 7 MediaType contentType = body.contentType(); 8 if (contentType != null) { 9 requestBuilder.header("Content-Type", contentType.toString()); 10 } 11 12 long contentLength = body.contentLength(); 13 if (contentLength != -1) { 14 requestBuilder.header("Content-Length", Long.toString(contentLength)); 15 requestBuilder.removeHeader("Transfer-Encoding"); 16 } else { 17 requestBuilder.header("Transfer-Encoding", "chunked"); 18 requestBuilder.removeHeader("Content-Length"); 19 } 20 21 request = requestBuilder.build(); 22 } 23 24 // Create the initial HTTP engine. Retries and redirects need new engine 25 // for each attempt. 26 engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null); 27 28 int followUpCount = 0; 29 while (true) { 30 if (canceled) { 31 engine.releaseConnection(); 32 throw new IOException("Canceled"); 33 } 34 35 try { 36 Timer requestTime = ThreadLocalMetricsRecorder.getInstance().getRequestTimer(); 37 Timer responseTime = ThreadLocalMetricsRecorder.getInstance().getResponseTimer(); 38 requestTime.start(); 39 engine.sendRequest(); 40 requestTime.stop(); 41 responseTime.start(); 42 engine.readResponse(); 43 responseTime.stop(); 44 } catch (RequestException e) { 45 // The attempt to interpret the request failed. Give up. 46 throw e.getCause(); 47 } catch (RouteException e) { 48 // The attempt to connect via a route failed. The request will 49 // not have been sent. 50 HttpEngine retryEngine = engine.recover(e); 51 if (retryEngine != null) { 52 engine = retryEngine; 53 continue; 54 } 55 // Give up; recovery is not possible. 56 throw e.getLastConnectException(); 57 } catch (IOException e) { 58 // An attempt to communicate with a server failed. The request 59 // may have been sent. 60 HttpEngine retryEngine = engine.recover(e, null); 61 if (retryEngine != null) { 62 engine = retryEngine; 63 continue; 64 } 65 66 // Give up; recovery is not possible. 67 throw e; 68 } 69 70 Response response = engine.getResponse(); 71 Request followUp = engine.followUpRequest(); 72 73 if (followUp == null) { 74 if (!forWebSocket) { 75 engine.releaseConnection(); 76 } 77 return response; 78 } 79 80 if (++followUpCount > MAX_FOLLOW_UPS) { 81 throw new ProtocolException("Too many follow-up requests: " + followUpCount); 82 } 83 84 if (!engine.sameConnection(followUp.url())) { 85 engine.releaseConnection(); 86 } 87 88 Connection connection = engine.close(); 89 request = followUp; 90 engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null, response); 91 } 92 }
算出首包,剩餘包時間和攔截重定向,經過重寫enqueue方法,獲取清求的錯誤與responseide
1 @Override 2 public void enqueue(Callback paramCallback) 3 { 4 // a(); 5 this.e.enqueue(new Callback() { 6 7 @Override 8 public void onResponse(Response response) throws IOException { 9 10 } 11 12 @Override 13 public void onFailure(Request request, IOException e) { 14 15 } 16 }); 17 }