1. 程式人生 > >android除錯stetho的那點事

android除錯stetho的那點事

在除錯安卓程式的過程中,受不了每次看日誌檢視網路的響應和匯出db的動作(甚至有時候都無法匯出db),這裡安利一個facebook出品的神器 stetho,不過這個有些限制,在使用的時候一定的通過usb與除錯的手機相連通的,也要使用chrome瀏覽器

1. stetho支援的功能

支援的功能主要是針對網路和db的,看github專案上的趨勢,應該是在準備一些後續的功能(按照需求整合咯,我覺得網路和db的最重要了),把官網的介紹摳了下來官網stetho介紹

1. 支援應用的網路請求返回報文的檢視

在chrome瀏覽器中輸入chrome://inspect來進入。(第一次使用這個功能的時候要翻牆,翻牆,翻牆,重要的事情要說三遍,不然你點選了inpect永遠是空白的) 進行檢視頁面

進行網路請求檢視頁面

2. 支援的db檢視功能 和 支援的sql語句直接進行互動功能(增刪改查都是可以的)

進行db檢視頁面

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的建議(乾貨)

  1. 建立一個單獨的productFlavor來整合功能,不要在正式的環境中整合這個東西。會使得應用變得更加龐大,也給應用留下漏洞 比如在build.gradle建立一個productflavor

     productFlavors {
         {
           
        }
    }
    

    在應用的的main目錄中建立一個productFlavor innerteset的目錄,然後把,通過清單合併操作中的替換application類的方式重新指定application. 為flavor建立單獨的application和清單檔案

    重新指定application

  2. 關於網路的請求中,有的應用的報文是有加解密的。這裡需要做一些額外的動作 修改預設的網路請求攔截類 StethoInterceptor.class,新建的一個類把原來的類檔案中東西拷貝出來進行調整 解密請求的報文,主要是修改內部類OkHttpInspectorRequestbody(),拿到原始報文的,請求體,完成解密動作後重新包裝生成一個請求體,給原來的程式碼使用。下面有一個我在自己應用中使用的例項

      @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();
    }
}