Stetho,一个功能强大的 Android 应用调试桥

What is Stetho ?

Stetho 是一个功能强大的 Android 应用调试桥,起到桥梁的作用,连接 Android 应用和 Chrome,通过 Chrome 开发者工具调试 Android 应用,提供视图元素检查,网络监控,数据库动态交互,Dumpapp(可扩展的命令行交互接口),JavaScript Console 等功能。

当启用后,开发者可以通过 Chrome 桌面浏览器中的开发者工具访问本地应用。开发者也可以选择启用可选的 dumpapp 工具提供一个强大的应用内部命令行接口。

官网:http://facebook.github.io/stetho/

项目地址:https://github.com/facebook/stetho

一旦你完成了下面的设置说明,只要启动你电脑上的 Chrome 浏览器并输入chrome://inspect点击 "Inspect" 按钮即可开始调试。

注意:如果你点击 "Inspect" 出现的是一个空白页面,请尝试下面的解决方案:

可能是 Chrome 版本过低,尝试升级 Chrome。

先翻个墙再打开调试界面(第一次需要这样,后面不需要)。

配置说明

添加 stetho 主依赖

在 Gradle 中包含 stetho

// Gradle dependency on Stetho

dependencies {

compile 'com.facebook.stetho:stetho:1.4.1'

}

在 Maven 中包含 stetho

com.facebook.stetho

stetho

1.4.1

只有 stetho 主依赖是必须的,但你可能还希望有一个网络助手

dependencies {

compile 'com.facebook.stetho:stetho-okhttp3:1.4.1'

}

或者:

dependencies {

compile 'com.facebook.stetho:stetho-okhttp:1.4.1'

}

或者:

dependencies {

compile 'com.facebook.stetho:stetho-urlconnection:1.4.1'

}

功能说明

Chrome DevTools

Stetho,一个功能强大的 Android 应用调试桥_第1张图片

image

Stetho 为你的应用提供了 C/S 协议实现,所以你可以通过 Chrome 集成的前端开发工具访问你的应用。只要你的应用集成了 Stetho,只需导航到chrome://inspect并点击 "Inspect" 即可开始使用。

Network Inspection

Stetho,一个功能强大的 Android 应用调试桥_第2张图片

image

使用 Chrome 开发者工具各种功能实现网络监控,包括图片预览,JSON 响应辅助工具,甚至把跟踪信息导出为 HAR 格式文件。

Database Inspection

Stetho,一个功能强大的 Android 应用调试桥_第3张图片

image

SQLite 数据库可视化与交互,具备完全读写功能,

Web SQL 下的是应用的数据库,点击数据库可以输入 SQL 语句对其进行操作

Local Storage 就是对应 Android 下的 SharedPreferences,可修改 SharedPreferences 中的值

View Hierarchy

Stetho,一个功能强大的 Android 应用调试桥_第4张图片

image

View Hierarchy 支持 API 15 或更高版本。使用 View Hierarchy 可以很方便的检查运行时界面的层次结构与数据,比如:

View Hierarchy 中包含界面所有元素的层次结构和属性

鼠标移动到 View Hierarchy 中某个 View,app 中对应的 View 会高亮显示

点击 View Hierarchy 左上角的搜索按钮,再点击 app 当前界面的控件,View Hierarchy 会显示该控件在层次中的位置

dumpapp

Stetho,一个功能强大的 Android 应用调试桥_第5张图片

image

Dumpapp 为应用提供了一个可扩展的命令行交互接口,提供了一组默认的插件,但是 dumpapp 的真正强大之处在于能够轻松创建自己的插件!

dumpapp 就在工程的 scripts/dumpapp 下,遗憾的是目前在 Windows 下还用不了,因为它只提供了 Linux/Mac 下的执行脚本。

常用命令(插件):

列出所有 Plugin :./scripts/dumpapp -p com.facebook.stetho.sample -l

打印 SharedPreferences :./scripts/dumpapp prefs print

写 SharedPreferences :./scripts/dumpapp prefs write

dumpapp 默认提供的插件就在com.facebook.stetho.dumpapp.plugins.*,具体使用方法可以参考源码中的说明。

JavaScript Console

Stetho,一个功能强大的 Android 应用调试桥_第6张图片

image

JavaScript Console 允许执行那些可以与应用或 Android SDK 交互的 JavaScript 代码。

Stetho 使用Rhino实现使用脚本方式调用 Java。

Rhino 是一个完全使用Java语言编写的开源JavaScript实现。Rhino通常用于在Java程序中,为最终用户提供脚本化能力。它被作为J2SE 6上的默认Java脚本化引擎。

Github:https://github.com/mozilla/rhino

官网:https://www.mozilla.org/rhino/

集成说明

1. 初始化

在你的 Application 初始化时候调用 Stetho 的初始化方法:

public class MyApplication extends Application {

public void onCreate() {

super.onCreate();

Stetho.initializeWithDefaults(this);

}

}

这将启用大多数默认配置,但不启用一些额外的钩子(启用网络监控需要注意)。 有关各个子系统的具体细节,请参见下文。

2. 启用网络监控

如果你使用的是 2.2.x+ 或 3.x 版本的 OkHttp 库,可以使用拦截器系统自动挂接到现有堆栈。 这是目前启用网络监控的最简单和最直接的方法。

For OkHttp 2.x

OkHttpClient client = new OkHttpClient();

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

For OkHttp 3.x

new OkHttpClient.Builder()

.addNetworkInterceptor(new StethoInterceptor())

.build();

由于拦截器可以修改请求和响应,应该在其他拦截器之后添加 Stetho 拦截器以获取准确的网络交互视图。

如果你使用HttpURLConnection,可以使用StethoURLConnectionManager来帮助集成,但该方法有一些注意事项,比如你必须明确地添加Accept-Encoding:gzip到请求头,并手动处理压缩的响应,以便 Stetho 报告压缩的有效负载大小。具体可以参考stetho-sample中的Networker的实现。

Stetho 目前没有提供 HttpClient 网络监控支持,具体原因可以查看issues 116(HttpClient 在 Android5.0 已经被废弃,不建议再使用)。

OkHttp + Retrofit

一般开发中我们都会使用OkHttp + Retrofit,OkHttp用于 HTTP 网络交互,Retrofit用于将 HTTP API 转换为 Java 接口。

默认情况下,Retrofit会自己创建一个OkHttpClient,我们也可以在创建Retrofit的时候通过client(OkHttpClient client)方法提供一个OkHttpClient。

sRetrofit = new Retrofit.Builder()

.baseUrl(baseUrl)

.client(sClient)

.build();

通过client(OkHttpClient client)方法设置共用一个OkHttpClient,监控Retrofit中的网络交互。

更多细节见stetho-sample项目。

3. 自定义 dumpapp 插件

自定义插件主要是实现 DumperPlugin 接口中的String getName()和void dump(DumperContext dumpContext)方法。getName()方法返回插件的名称,dump(DumperContext dumpContext)是命令行中调用该插件时的回调方法。其中,dumpContext.getStdout()获取命令行输出,dumpContext.getArgsAsList()获取命令行调用的参数列表。

public class MyDumperPlugin implements DumperPlugin {

private static final String XML_SUFFIX = ".xml";

private static final String NAME = "prefs";

private final Context mAppContext;

public MyDumperPlugin(Context context) {

mAppContext = context.getApplicationContext();

}

@Override

public String getName() {

return NAME;

}

@Override

public void dump(DumperContext dumpContext) throws DumpUsageException {

PrintStream writer = dumpContext.getStdout();

List args = dumpContext.getArgsAsList();

String commandName = args.isEmpty() ? "" : args.remove(0);

if (commandName.equals("print")) {

doPrint(writer, args);

} else if (commandName.equals("write")) {

doWrite(args);

} else {

doUsage(writer);

}

}

// 省略部分代码

}

然后把初始化调用替换如下:

Stetho.initialize(Stetho.newInitializerBuilder(context)

.enableDumpapp(new DumperPluginsProvider() {

@Override

public Iterable get() {

return new Stetho.DefaultDumperPluginsBuilder(context)

.provide(new MyDumperPlugin())

.finish();

}

})

.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))

.build());

更多细节见stetho-sample项目。

4. 启用JavaScript Console

启用 JavaScript Console 只需在 build.gradle 中添加如下依赖即可:

compile "com.facebook.stetho:stetho-js-rhino:1.4.1"

启动 app,在 Chrome 开发者工具的 Console 输入下面代码使 app 打印一个Toast:

importPackage(android.widget);

importPackage(android.os);

var handler = new Handler(Looper.getMainLooper());

handler.post(function() { Toast.makeText(context, "hello", Toast.LENGTH_LONG).show() });

Stetho,一个功能强大的 Android 应用调试桥_第7张图片

Paste_Image.png

importPackage(android.widget)等于 java 中import android.widget.*;,JavaScript 中使用 var 定义变量,这段代码就是创建了一个 handler 并调用 post 方法在 ui 线程弹一个 Toast。

关于 Rhino 相关语法可以参考下面的文档

Scripting Java

Performance Hints

在Toast.makeText中的 context 是从哪里来的呢?

context 是在com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder的initJsScope方法中被绑定到JSContext的,下面是 initJsScope 方法的源码:

private @NonNull ScriptableObject initJsScope(@NonNull Context jsContext) {

// Set the main Rhino goodies

ImporterTopLevel importerTopLevel = new ImporterTopLevel(jsContext);

ScriptableObject scope = jsContext.initStandardObjects(importerTopLevel, false);

ScriptableObject.putProperty(scope, "context", Context.javaToJS(mContext, scope));

try {

importClasses(jsContext, scope);

importPackages(jsContext, scope);

importConsole(scope);

importVariables(scope);

importFunctions(scope);

} catch (StethoJsException e) {

String message = String.format("%s\n%s", e.getMessage(), Log.getStackTraceString(e));

LogUtil.e(e, message);

CLog.writeToConsole(Console.MessageLevel.ERROR, Console.MessageSource.JAVASCRIPT, message);

}

return scope;

}

JsRuntimeReplFactoryBuilder提供了一些方法可以传递自己的变量,类,包和函数到 JavaScript 环境。

添加变量,类,包和函数到 JavaScript 运行时

修改初始化代码如下:

Stetho.initialize(Stetho.newInitializerBuilder(context)

.enableWebKitInspector(new ExtInspectorModulesProvider(context))

.build());

private static class ExtInspectorModulesProvider implements InspectorModulesProvider {

private Context mContext;

private final Handler handler = new Handler(Looper.getMainLooper());

ExtInspectorModulesProvider(Context context) {

mContext = context;

}

@Override

public Iterable get() {

return new Stetho.DefaultInspectorModulesBuilder(mContext)

.runtimeRepl(new JsRuntimeReplFactoryBuilder(mContext)

// 添加变量

.addVariable("test", new AtomicBoolean(true))

// 添加类

.importClass(R.class)

// 添加包

.importPackage(MyApplication.class.getPackage().getName())

// 添加方法到 javascript: void toast(String)

.addFunction("toast", new BaseFunction() {

@Override

public Object call(org.mozilla.javascript.Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {

// javascript 传递的参数在 varags

final String message = args[0].toString();

handler.post(new Runnable() {

@Override

public void run() {

Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();

}

});

// 在 javascript 返回 undefined

return org.mozilla.javascript.Context.getUndefinedValue();

}

})

.build())

.finish();

}

}

说明:Java原语类型将被自动装箱,只有对象可以传递到 JavaScript 运行时。

绑定完成后就可以在 JavaScript Console 中使用自己的变量,类,包和函数了。

Stetho,一个功能强大的 Android 应用调试桥_第8张图片

Paste_Image.png

注意:Rhino 对包名的检查是严格的,必须是com.**,org.**,net.**之类比较正规的格式。假如:包名使用linchaolong.stetho.demo,在importClasses(linchaolong.stetho.demo.R)时,ScriptRuntime会报EcmaError

Failed to import class: linchaolong.stetho.demo.R

com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder$StethoJsException: Failed to import class: linchaolong.stetho.demo.R

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.importClasses(JsRuntimeReplFactoryBuilder.java:195)

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.initJsScope(JsRuntimeReplFactoryBuilder.java:173)

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.initJsScope(JsRuntimeReplFactoryBuilder.java:158)

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.access$000(JsRuntimeReplFactoryBuilder.java:45)

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder$1.newInstance(JsRuntimeReplFactoryBuilder.java:146)

at com.facebook.stetho.inspector.protocol.module.Runtime$Session.getRepl(Runtime.java:271)

at com.facebook.stetho.inspector.protocol.module.Runtime$Session.evaluate(Runtime.java:260)

at com.facebook.stetho.inspector.protocol.module.Runtime.evaluate(Runtime.java:158)

at java.lang.reflect.Method.invoke(Native Method)

at java.lang.reflect.Method.invoke(Method.java:372)

at com.facebook.stetho.inspector.MethodDispatcher$MethodDispatchHelper.invoke(MethodDispatcher.java:96)

at com.facebook.stetho.inspector.MethodDispatcher.dispatch(MethodDispatcher.java:67)

at com.facebook.stetho.inspector.ChromeDevtoolsServer.handleRemoteRequest(ChromeDevtoolsServer.java:129)

at com.facebook.stetho.inspector.ChromeDevtoolsServer.handleRemoteMessage(ChromeDevtoolsServer.java:111)

at com.facebook.stetho.inspector.ChromeDevtoolsServer.onMessage(ChromeDevtoolsServer.java:87)

at com.facebook.stetho.websocket.WebSocketSession$1.handleTextFrame(WebSocketSession.java:176)

at com.facebook.stetho.websocket.WebSocketSession$1.onCompleteFrame(WebSocketSession.java:136)

at com.facebook.stetho.websocket.ReadHandler.readLoop(ReadHandler.java:44)

at com.facebook.stetho.websocket.WebSocketSession.handle(WebSocketSession.java:45)

at com.facebook.stetho.websocket.WebSocketHandler.doUpgrade(WebSocketHandler.java:117)

at com.facebook.stetho.websocket.WebSocketHandler.handleRequest(WebSocketHandler.java:83)

at com.facebook.stetho.server.http.LightHttpServer.dispatchToHandler(LightHttpServer.java:84)

at com.facebook.stetho.server.http.LightHttpServer.serve(LightHttpServer.java:61)

at com.facebook.stetho.inspector.DevtoolsSocketHandler.onAccepted(DevtoolsSocketHandler.java:52)

at com.facebook.stetho.server.ProtocolDetectingSocketHandler.onSecured(ProtocolDetectingSocketHandler.java:63)

at com.facebook.stetho.server.SecureSocketHandler.onAccepted(SecureSocketHandler.java:33)

at com.facebook.stetho.server.LazySocketHandler.onAccepted(LazySocketHandler.java:36)

at com.facebook.stetho.server.LocalSocketServer$WorkerThread.run(LocalSocketServer.java:167)

Caused by: org.mozilla.javascript.EcmaError: ReferenceError: "linchaolong" is not defined. (chrome#1)

at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3949)

at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3927)

at org.mozilla.javascript.ScriptRuntime.notFoundError(ScriptRuntime.java:4012)

at org.mozilla.javascript.ScriptRuntime.name(ScriptRuntime.java:1849)

at org.mozilla.javascript.Interpreter.interpretLoop(Interpreter.java:1558)

at org.mozilla.javascript.Interpreter.interpret(Interpreter.java:815)

at org.mozilla.javascript.InterpretedFunction.call(InterpretedFunction.java:109)

at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:393)

at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3280)

at org.mozilla.javascript.InterpretedFunction.exec(InterpretedFunction.java:120)

at org.mozilla.javascript.Context.evaluateString(Context.java:1191)

at com.facebook.stetho.rhino.JsRuntimeReplFactoryBuilder.importClasses(JsRuntimeReplFactoryBuilder.java:193)

... 27 more

更多细节请参考stetho-js-rhino项目中的README.md

只在debug模式下使用 Stetho

修改 dependencies 配置,只在 debug 模式下编译stetho和stetho-js-rhino

dependencies {

// Debug

debugCompile "com.facebook.stetho:stetho:${stetho}"

compile "com.facebook.stetho:stetho-okhttp3:${stetho}"

debugCompile "com.facebook.stetho:stetho-js-rhino:${stetho}"

}

说明:${stetho}是 stetho 的版本号。

在src/debug/java目录下新建一个DebugApplication继承自MyApplication,并把初始化 Stetho 的代码移到DebugApplication

public class DebugApplication extends MyApplication{

@Override public void onCreate() {

super.onCreate();

Stetho.initializeWithDefaults(this);

}

在src/debug目录下创建一个AndroidManifest.xml,并添加 debug 模式下需要的权限和修改 application 节点android:name值为 DebugApplication(使用tools:replace覆盖android:name字段)


xmlns:tools="http://schemas.android.com/tools"

package="com.facebook.stetho.sample">

tools:replace="android:name"

android:name=".DebugApplication" />

Stetho,一个功能强大的 Android 应用调试桥_第9张图片

Paste_Image.png

完成上面处理后,Stetho 只在 debug 版本下起作用,不影响 release 版本。

作者:linchaolong

链接:http://www.jianshu.com/p/38d8324b126a

來源:

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(Stetho,一个功能强大的 Android 应用调试桥)