Android开发调试神器 - Stetho2

1,393 阅读3分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

接入其实很简单

再简单的接入也总有1234步,这里简单叨逼叨逼几句。

gradle配置

这里不说mvn和low逼的下载&拷贝库的方式了(拷贝源代码的方式集成就更不能忍了),直接上gradle配置

// Gradle dependency on Stetho 
  dependencies { 
    compile 'com.facebook.stetho:stetho:1.3.1' 
  } 

如果你使用了Okhttp 3.x的网络栈,请集成如下网络工具库

dependencies { 
    compile 'com.facebook.stetho:stetho-okhttp3:1.3.1' 
  } 

Okhttp 2.2.x+

dependencies { 
    compile 'com.facebook.stetho:stetho-okhttp:1.3.1' 
  } 

如果使用的是HttpURLConnection

dependencies { 
    compile 'com.facebook.stetho:stetho-urlconnection:1.3.1' 
  } 

小白兔和大灰狼请注意:

  • 如果你使用的是Apache HttpClient,对不起,你out了,请自行升级网络栈,当然你也可以在了解了Stetho的玩法之后自己写一套网络监控来适配Apache HttpClient。
  • 如果你使用的网络栈不在上面列举的里面,或者你用c/c++写的网络操作,又或者你采用的协议不是http/https的,那么,网络这部分的诊断和监控方法,估计是很难用了。还想用,自己写咯。

初始化

需要写的代码其实很少,而且几乎所有的应用都是一样的代码,首先是在Application类中初始化

public class MyApplication extends Application {
  public void onCreate() {
    super.onCreate();
    Stetho.initializeWithDefaults(this);
  }
}

这个初始化会开启大部分的听诊模块,但是网络监控等一些附加的钩子模块除外

网络诊断

如果你使用的网络栈是OkHttp,而且版本区间在2.2.x+到3.x,那么想要打开网络诊断模块,只需要在程序合适的位置调用如下代码即可

OkHttp 2.2.x+

OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());

OkHttp 3.x

new OkHttpClient.Builder()
    .addNetworkInterceptor(new StethoInterceptor())
    .build();

HttpURLConnection

如果你使用的HttpURLConnection,稍微有些麻烦的说,你可以使用Stetho框架SDK提供的类StethoURLConnectionManager来完成客户端网络诊断的开启,但是有一些坑是要注意的。

比如为了让Stetho向开发机上的Chrome汇报正确的经过压缩的有效载荷的大小,你需要亲自在http/https的请求头加上 "Accept-Encoding: gzip" ,并且自己处理压缩过的响应数据。如果采用Okhttp,这些都不必劳烦您老人家操心了,框架默认帮你考虑了。

参考代码如下:

private final StethoURLConnectionManager stethoManager;

private static final int READ_TIMEOUT_MS = 10000;
private static final int CONNECT_TIMEOUT_MS = 15000;

private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
private static final String GZIP_ENCODING = "gzip";

private String url = "http://www.figotan.org";
stethoManager = new StethoURLConnectionManager(url);

URL url = new URL(url);

// Note that this does not actually create a new connection so it is appropriate to
// defer preConnect until after the HttpURLConnection instance is configured.  Do not
// invoke connect, conn.getInputStream, conn.getOutputStream, etc before calling
// preConnect!
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
try {
    conn.setReadTimeout(READ_TIMEOUT_MS);
    conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
    conn.setRequestMethod(request.method.toString());

    // Adding this disables transparent gzip compression so that we can intercept
    // the raw stream and display the correct response body size.
    conn.setRequestProperty(HEADER_ACCEPT_ENCODING, GZIP_ENCODING);

    SimpleRequestEntity requestEntity = null;
    if (request.body != null) {
        requestEntity = new ByteArrayRequestEntity(request.body);
    }

    stethoManager.preConnect(conn, requestEntity);
    try {
          if (request.method == HttpMethod.POST) {
            if (requestEntity == null) {
              throw new IllegalStateException("POST requires an entity");
            }
            conn.setDoOutput(true);
            requestEntity.writeTo(conn.getOutputStream());
          }

          // Ensure that we are connected after this point.  Note that getOutputStream above will
          // also connect and exchange HTTP messages.
          conn.connect();

          stethoManager.postConnect();

    } catch (IOException inner) {
          // This must only be called after preConnect.  Failures before that cannot be
          // represented since the request has not yet begun according to Stetho.
          stethoManager.httpExchangeFailed(inner);
          throw inner;
    }
} catch (IOException outer) {
        conn.disconnect();
        throw outer;
}
      
try {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    InputStream rawStream = conn.getInputStream();
    try {
        // Let Stetho see the raw, possibly compressed stream.
        rawStream = stethoManager.interpretResponseStream(rawStream);
        if (rawStream != null && GZIP_ENCODING.equals(conn.getContentEncoding())) {
            decompressedStream  = new GZIPInputStream(in);
        } else {
            decompressedStream = rawStream;
        } 
    
        if (decompressedStream != null) {
            int n;
            byte[] buf = new byte[1024];
            while ((n = decompressedStream.read(buf)) != -1) {
                out.write(buf, 0, n);
            }
        }
    } finally {
        if (rawStream != null) {
            rawStream.close();
          }
    }
} finally {
    conn.disconnect();
}

通过如上步骤,已经可以让你的APP支持网络监控,数据库监控和SharedPreferences文件内容监控了。如果想玩更高级的,请看后面的自定义dumpapp插件和Rhino,如果想直接玩起来,请继续往下看。