What is Stetho ?

Stetho is a powerful Android app debugging bridge, which acts as a bridge between Android apps and Chrome. It provides view element checking, network monitoring, database dynamic interaction, and so on. Dumpapp (extensible command line interface), JavaScript Console, and more.

When enabled, developers can access native apps through developer tools in the Chrome desktop browser. Developers can also choose to enable the optional Dumpapp tool to provide a powerful in-app command line interface.

  • Liverpoolfc.tv: facebook. Making. IO/stetho /
  • Project address: github.com/facebook/st…

Once you’ve completed the setup instructions below, just launch Chrome on your computer and type Chrome ://inspect and click the “inspect” button to start debugging.

Configuration instructions

  1. Add the Stetho primary dependency

    Include Stetho in Gradle

    / / Gradle dependency on Stetho dependencies {the compile 'com. Facebook. Stetho: Stetho: 1.4.1'}Copy the code

    Stetho is included in Maven

    Com. Facebook. Stetho stetho 1.4.1Copy the code
  2. Only the Stetho master dependency is required, but you may also want to have a web assistant

    Dependencies {the compile 'com. Facebook. Stetho: stetho - okhttp3:1.4.1'}Copy the code

    Or:

    Dependencies {the compile 'com. Facebook. Stetho: stetho - okhttp: 1.4.1'}Copy the code

    Or:

    Dependencies {the compile 'com. Facebook. Stetho: stetho - urlconnection: 1.4.1'}Copy the code

Functional specifications

Chrome DevTools




image

Stetho provides a C/S protocol implementation for your application, so you can access your application through Chrome’s integrated front-end development tools. As long as your app is integrated with Stetho, just navigate to Chrome ://inspect and click “inspect” to get started.

Network Inspection




image

Use Chrome Developer Tools for network monitoring, including image previews, JSON response AIDS, and even exporting trace information as HAR files.

Database Inspection




image

SQLite database visualization and interaction, with full read and write functions,

  • Under Web SQL is the application database. Click the database to enter SQL statements to operate it
  • Local Storage is the SharedPreferences under Android. You can modify the value of SharedPreferences

View Hierarchy




image

View Hierarchy supports API 15 or higher. View Hierarchy makes it easy to check interface elements, such as:

  1. View Hierarchy contains the Hierarchy and attributes of all elements of the interface
  2. Move the mouse pointer to a View in View Hierarchy, and the corresponding View in app will be highlighted
  3. Click the search button in the upper left corner of View Hierarchy, and then click the control in the current interface of app. View Hierarchy will display the position of the control in the Hierarchy

dumpapp




image

Dumpapp provides an extensible command-line interface for applications and a default set of plug-ins, but the real power of Dumpapp is the ease with which you can create your own plug-ins!

Dumpapp is under project scripts/dumpapp, unfortunately it is not currently available on Windows as it only provides Linux/Mac execution scripts.

Common commands (plug-ins) :

  • List all plugins:./scripts/dumpapp -p com.facebook.stetho.sample -l
  • Print SharedPreferences:./scripts/dumpapp prefs print
  • Write SharedPreferences:./scripts/dumpapp prefs write

Dumpapp default provide plug-in on the com. Facebook. Stetho. Dumpapp. Plugins. *, specific usage can refer to the source code in the description.

JavaScript Console




image

The JavaScript Console allows you to execute JavaScript code that can interact with your application or the Android SDK.

Stetho uses a Rhino implementation to invoke Java in a scripted manner.

Rhino is an open source JavaScript implementation written entirely in the Java language. Rhino is typically used in Java programs to provide scripting capabilities to end users. It is used as the default Java scripting engine on J2SE 6.

  • Github:github.com/mozilla/rhi…
  • Website: www.mozilla.org/rhino/

Integrated description

1. Initialization

Call Stetho’s initialization method when your Application initializes:

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

This will enable most of the default configurations, but will not enable some additional hooks (note the need to enable network monitoring). See below for specific details on each subsystem.

2. Enable network monitoring

If you are using a 2.2.x+ or 3.x version of the OkHttp library, you can use the interceptor system to automatically hook into the existing stack. This is currently the simplest and most straightforward way to enable network monitoring.

For OkHttp 2.x

OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());Copy the code

For OkHttp 3.x

new OkHttpClient.Builder()
    .addNetworkInterceptor(new StethoInterceptor())
    .build();Copy the code

Because interceptors can modify requests and responses, Stetho interceptors should be added after other interceptors to get an accurate view of network interactions.

If you use HttpURLConnection, StethoURLConnectionManager are available to help integrate, but this method has some matters needing attention, for example, you must explicitly add the Accept – Encoding: Gzip to the request header and manually process the compressed response so that Stetho reports the compressed payload size. For details, see the implementation of Networker in Stetho-Sample.

Stetho does not currently provide support for HttpClient network monitoring. See Issues 116 for details.

OkHttp + Retrofit

In general development we use OkHttp + Retrofit, OkHttp for HTTP network interaction, and Retrofit for converting HTTP apis to Java interfaces.

By default, Retrofit creates an OkHttpClient by itself, or we can provide an OkHttpClient when we create Retrofit through the Client (OkHttpClient Client) method.

    sRetrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(sClient)
        .build();Copy the code

Network interactions in Retrofit are monitored by setting up a common OkHttpClient through the Client (OkHttpClient Client) method.

See the Stetho-Sample project for more details.

3. Customize the Dumpapp plug-in

Custom plug-ins mainly implement String getName() and void dump(DumperContext dumpContext) methods in the DumperPlugin interface. The getName() method returns the name of the plug-in, and dump(DumperContext dumpContext) is the callback method when the plug-in is invoked on the command line. . Among them, the dumpContext getStdout () to obtain the command-line output, dumpContext. GetArgsAsList () to obtain a list of command-line invocation parameters.

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); }} // omit some code}Copy the code

Then replace the initial call with the following:

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());Copy the code

See the Stetho-Sample project for more details.

4. Enable the JavaScript Console

To enable JavaScript Console, simply add the following dependencies to build.gradle:

The compile "com. Facebook. Stetho: stetho - js - rhino: 1.4.1." "Copy the code

Start the app and type the following code on the Console of Chrome Developer Tools to make the app print a Toast:

importPackage(android.widget);
importPackage(android.os);
var handler = new Handler(Looper.getMainLooper());
handler.post(function() { Toast.makeText(context, "hello", Toast.LENGTH_LONG).show() });Copy the code



Paste_Image.png

ImportPackage (Android.widget) = import Android.widget.*; JavaScript uses var to define variables. This code creates a handler and calls the POST method to play a Toast in the UI thread.

Refer to the documentation below for Rhino syntax

  • Scripting Java
  • Performance Hints

inToast.makeTextWhere does the context come from in?

The context is the com facebook. Stetho. Rhino. JsRuntimeReplFactoryBuilder initJsScope approach is bound to JSContext, Here is the source code for the initJsScope method:

  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;
  }Copy the code

JsRuntimeReplFactoryBuilder provides methods can pass their own variables, classes, packages and functions to the JavaScript environment.

Add variables, classes, packages, and functions to the JavaScript runtime

Modify the initialization code as follows:

Stetho.initialize(Stetho.newInitializerBuilder(context) .enableWebKitInspector(new ExtInspectorModulesProvider(context))  .build());Copy the code
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) / / add variables. AddVariable (" test ", New AtomicBoolean (true)) / / add classes. ImportClass (R.c lass) / / add packages. The importPackage (MyApplication. Class. GetPackage (). The getName ()) // Add methods to javascript: void toast(String) .addFunction("toast", new BaseFunction() { @Override public Object call(org.mozilla.javascript.Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {varags final String Message = args[0].toString(); handler.post(new Runnable() { @Override public void run() { Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();  }}); / / in the javascript returns undefined return org. Mozilla. Javascript. Context. GetUndefinedValue (); } }) .build()) .finish(); }}Copy the code

Note: Java primitive types are automatically boxed, and only objects can be passed to the JavaScript runtime.

Once the binding is complete, you can use your own variables, classes, packages, and functions in the JavaScript Console.




Paste_Image.png

Note: Rhino is strict about checking package names and must be in formal formats such as com.**, org.**, net.**. If: the package name to use linchaolong. Stetho. Demo, in importClasses (linchaolong. Stetho. Demo. R), will quote EcmaError ScriptRuntime

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 moreCopy the code

Refer to readme.md in the Stetho-js-Rhino project for more details

Use Stetho only in Debug mode

Modify the Dependencies configuration to compile stetho and Stetho-js-Rhino only in debug mode

dependencies {
  // Debug
  debugCompile "com.facebook.stetho:stetho:${stetho}"
  compile "com.facebook.stetho:stetho-okhttp3:${stetho}"
  debugCompile "com.facebook.stetho:stetho-js-rhino:${stetho}"
}Copy the code

${stetho} is the version number of stetho.

Create a new DebugApplication inheriting from MyApplication in the SRC /debug/ Java directory and move the Stetho initialization code to the DebugApplication

public class DebugApplication extends MyApplication{

  @Override public void onCreate() {
    super.onCreate();
    Stetho.initializeWithDefaults(this);
  }Copy the code

Create an Androidmanifest.xml in the SRC /debug directory, Add permissions required in debug mode and change the application node android:name value to the DebugApplication (use tools: Replace to override the Android: Name field).




  

  

Copy the code



Paste_Image.png

After the above processing, Stetho only works in the Debug version, not the Release version.