android除錯stetho的那點事
在除錯安卓程式的過程中,受不了每次看日誌檢視網路的響應和匯出db的動作(甚至有時候都無法匯出db),這裡安利一個facebook出品的神器 stetho,不過這個有些限制,在使用的時候一定的通過usb與除錯的手機相連通的,也要使用chrome瀏覽器
1. stetho支援的功能
支援的功能主要是針對網路和db的,看github專案上的趨勢,應該是在準備一些後續的功能(按照需求整合咯,我覺得網路和db的最重要了),把官網的介紹摳了下來官網stetho介紹
1. 支援應用的網路請求返回報文的檢視
在chrome瀏覽器中輸入chrome://inspect
來進入。(第一次使用這個功能的時候要翻牆,翻牆,翻牆,重要的事情要說三遍,不然你點選了inpect永遠是空白的)
2. 支援的db檢視功能 和 支援的sql語句直接進行互動功能(增刪改查都是可以的)
2. 整合stetho
-
匯入依賴 implementation ‘com.facebook.stetho:stetho:1.5.0’
-
根據網路請求框架匯入不同的依賴包 implementation ‘com.facebook.stetho:stetho-okhttp3:1.5.0’ or: implementation ‘com.facebook.stetho:stetho-urlconnection:1.5.0’
-
在application中進行整合
public class MyApplication extends Application { public void onCreate() { super.onCreate(); Stetho.initializeWithDefaults(this); } }
- 如果是有網路請求的,以okhttp舉例,建立okhttpClient的時候需要加入一個攔截器
new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor()) .build()
3. 整合stetho的建議(乾貨)
-
建立一個單獨的productFlavor來整合功能,不要在正式的環境中整合這個東西。會使得應用變得更加龐大,也給應用留下漏洞 比如在build.gradle建立一個productflavor
productFlavors { { } }
在應用的的main目錄中建立一個productFlavor innerteset的目錄,然後把,通過清單合併操作中的替換application類的方式重新指定application.
-
關於網路的請求中,有的應用的報文是有加解密的。這裡需要做一些額外的動作 修改預設的網路請求攔截類
StethoInterceptor.class
,新建的一個類把原來的類檔案中東西拷貝出來進行調整 解密請求的報文,主要是修改內部類OkHttpInspectorRequest
的body()
,拿到原始報文的,請求體,完成解密動作後重新包裝生成一個請求體,給原來的程式碼使用。下面有一個我在自己應用中使用的例項@Nullable public byte[] body() throws IOException { //我的測試應用的請求報文都是data:{}的格式,所以這裡這麼寫,各個應用要按照自己應用的需求改寫 FormBody copyedBody = (FormBody) (this.mRequest.body()); List<String> nameList = new ArrayList<>(); List<String> valusList = new ArrayList<>(); for (int i=0; i< copyedBody.size(); i++) { nameList.add(copyedBody.encodedName(i)); if ("data".equals(copyedBody.encodedName(i))) { valusList.add(new JsonFormatUtil().formart(這裡解密請求的報文)); } } FormBody copyedBody2 = new FormBody.Builder().add(nameList.get(0), valusList.get(0)).build(); FormBody body = copyedBody2; //下面這塊程式碼不動,保持原樣,上面重新生成了requestBody而已 if (body == null) { return null; } else { OutputStream out = this.mRequestBodyHelper.createBodySink(this.firstHeaderValue("Content-Encoding")); BufferedSink bufferedSink = Okio.buffer(Okio.sink(out)); try { body.writeTo(bufferedSink); } finally { bufferedSink.close(); } return this.mRequestBodyHelper.getDisplayBody(); } }
解密返回報文,返回的報文,stetho是儲存在檔案中的然後進行的傳送,需要修改預設的
ResponseHandler
抄襲原來的ReponseHanlder,主要修改的onEOF方式
//調整原來的類,增加一個readFile的方法 public void onEOF() { this.reportDataReceived(); try { readFile(this.mRequestId); } catch (IOException e) { Log.e(TAG, "readFile Exception onEOF: " + e); } this.mEventReporter.responseReadFinished(this.mRequestId); } //讀取預設的檔案 public ResponseBodyData readFile(String requestId) throws IOException { ResponseBodyFileManager responseBodyFileManager = new ResponseBodyFileManager(CeshiApplication.getApplication()); ResponseBodyData responseBodyData = responseBodyFileManager.readFile(requestId); OutputStream outputStream = null; //這個物件是資料的物件,用於json轉換使用 SfReponseBodyData sfReponseBodyData = new Gson().fromJson(responseBodyData.data, SfReponseBodyData.class); sfReponseBodyData.data = 這裡就可以進行解密的動作,得到解密的字串; try { outputStream = responseBodyFileManager.openResponseBodyFile(requestId, responseBodyData.base64Encoded); String data = new Gson().toJson(sfReponseBodyData); data = data.replace("\\", ""); data = new JsonFormatUtil().formart(data); outputStream.write(data.getBytes()); } catch (Exception e) { Log.e(TAG, "readFile Exception: " + e); } finally { if (null != outputStream) { outputStream.close(); } } LogUtils.getInstance().showLogD(TAG, "readFile" ,"new record"); return null; }
為了在瀏覽器上好看,報文最後都需要進行格式化,比如我這裡是預設的json報文,就進行格式化後傳給瀏覽器
//網路上隨便摳的一段格式程式碼
public class JsonFormatUtil {
public String formart(String s) {
int level = 0;
//存放格式化的json字串
StringBuilder jsonForMatStr = new StringBuilder();
for (int index = 0; index < s.length(); index++)//將字串中的字元逐個按行輸出
{
//獲取s中的每個字元
char c = s.charAt(index);
//level大於0並且jsonForMatStr中的最後一個字元為\n,jsonForMatStr加入\t
if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) {
jsonForMatStr.append(getLevelStr(level));
}
//遇到"{"和"["要增加空格和換行,遇到"}"和"]"要減少空格,以對應,遇到","要換行
switch (c) {
case '{':
case '[':
jsonForMatStr.append(c + "\n");
level++;
break;
case ',':
jsonForMatStr.append(c + "\n");
break;
case '}':
case ']':
jsonForMatStr.append("\n");
level--;
jsonForMatStr.append(getLevelStr(level));
jsonForMatStr.append(c);
break;
default:
jsonForMatStr.append(c);
break;
}
}
return jsonForMatStr.toString();
}
private static String getLevelStr(int level) {
StringBuilder levelStr = new StringBuilder();
for (int levelI = 0; levelI < level; levelI++) {
levelStr.append("\t");
}
return levelStr.toString();
}
}