The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Stetho

In 2015, Facebook announced Stetho, “a new debugging platform for Android”. That description is more apt than you might think, in that Stetho allows you to examine your view hierarchy, see network requests, and otherwise analyze your project… using Chrome.

Wait, Wut? Chrome?

Many modern Web browsers have Web client debugging tools, either built into the browser itself or available as an extension or other add-on. These tools can let you browse the content of the Web page, see network requests, and otherwise analyze the content of a browser tab.

Stetho leverages the Chrome Developer Tools, available in Chrome and Chromium, to have those tools examine an Android app, rather than a browser tab.

This works by way of Chrome Developer Tools’ support for remote debugging. Stetho basically embeds a small server in your app that speaks the same protocol that Chrome Developer Tools uses for remote debugging. From Chrome’s perspective, your Android app is just another Web browser. In reality, Stetho translates Chrome Developer Tools’ requests (e.g., “give me your DOM”) into things that would help an Android developer (e.g., “give me your view hierarchy”).

Basic Stetho Integration

Stetho is not that hard to integrate, though Facebook’s documentation for it would have you ship Stetho in production in a release build, which is not an especially good idea.

The Diagnostics/Stetho sample project will show you how to hook Stetho up to your app, in a way that allows you to ship Stetho only in your debug builds. Overall, this app is yet another variant on the “show the latest android questions from Stack Overflow” introduced originally in the chapter on Internet access. This particular one uses Retrofit 2.x and Picasso for the network requests… though as you will see, we have both of those libraries delegate the actual network I/O to OkHttp 3.x.

Adding the Stetho Dependency

The official Stetho dependency is com.facebook.stetho:stetho, for some version (e.g., 1.4.2). However, if you want to allow Stetho to help you debug your network requests, you will need a different dependency, based on which HTTP client API you are using for those network requests:

HTTP Client API Stetho Dependency
OkHttp com.facebook.stetho:stetho-okhttp
OkHttp 3.x com.facebook.stetho:stetho-okhttp3
HttpURLConnection com.facebook.stetho:stetho-urlconnection

Those each have com.facebook.stetho:stetho as a transitive dependency, so you only need one Stetho dependency, based on your HTTP client API.

However, there are two issues:

  1. There is a bug in Stetho 1.4.2, where they forgot a particular dependency on part of the Android Support libraries
  2. Stetho 1.4.2 has a transitive dependency on appcompat-v7, for no obvious reason

So, the app/build.gradle file from the Stetho sample project cleans that up:

dependencies {
    implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
    implementation 'com.squareup.okhttp3:okhttp:3.8.0'
    implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
    debugCompile('com.facebook.stetho:stetho-okhttp3:1.4.2') {
        exclude group: 'com.android.support', module: 'appcompat-v7'
    }
    debugCompile 'com.android.support:support-compat:25.2.0'
}

Specifically, we are using the stetho-okhttp3 dependency, so we get Stetho plus its OkHttp 3.x support.

We use debugCompile so that these libraries are only used in debug builds, not release builds. We block the unnecessary appcompat-v7 transitive dependency via a Gradle exclude directive, while adding a debugCompile statement for support-compat, a part of the support-v4 library group that contains the missing support classes.

If you use Stetho in a project that is using appcompat-v7 itself, you might still want to use the exclude directive when pulling in Stetho, to help ensure that you get the version of appcompat-v7 that you want, not the one that Stetho wants.

Creating a Debug Sourceset

Ideally, as much of our Stetho-specific stuff as possible should not be in the main source set, as that is what ships to customers. Using debugCompile keeps the Stetho dependencies out of a release build, but we are going to need some code to initialize and configure Stetho. That code, ideally, goes somewhere other than main.

As was covered in the chapter on the Gradle project structure, we can have a debug source set, as a peer of main. Everything in debug will be merged into our project for a debug build but will be ignored with a release build. So, other than the debugCompile statements in app/build.gradle, the rest of our Stetho stuff will go into debug.

Adding the Stetho Application

Stetho requires some initialization work, and Facebook recommends that this be done in a custom Application object. This is a process-wide singleton, initialized when our process is forked, and so it is good for one-time, process-scope initialization work.

However, we really want this to be in the debug source set, and that requires a little bit of work.

The Main Application

Over in the main source set, we have an App class that extends Application and provides initialization for all build types:

package com.commonsware.android.stetho;

import android.app.Application;
import okhttp3.OkHttpClient;

public class App extends Application {
  private OkHttpClient ok;

  @Override
  public void onCreate() {
    super.onCreate();

    ok=buildOkBuilder().build();
  }

  OkHttpClient getOk() {
    return(ok);
  }

  protected OkHttpClient.Builder buildOkBuilder() {
    return(new OkHttpClient.Builder());
  }
}

Here, we are initializing an app-wide instance of OkHttpClient, to be used for our network requests. In other incarnations of this sample app, we either create the OkHttpClient directly in QuestionsFragment (where the network I/O is triggered) or are not using OkHttp at all. Here, we are doing this at the process level, for two reasons:

  1. For a more complex app, where you are doing network I/O in several places, you may want a single OkHttpClient instance with all of your configuration.
  2. Stetho needs access to the OkHttpClient, if we want it to report on network access. And, we need to use that same Stetho-configured OkHttpClient for all our network access that we want Stetho to report.

Note that we have a protected method, buildOkBuilder(), that sets up the OkHttpClient.Builder that we use to create the OkHttpClient instance. We will see that method again shortly… in a Stetho-specific subclass.

The main edition of the manifest then says that we should use App by setting android:name on <application>:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.commonsware.android.stetho"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-permission android:name="android.permission.INTERNET"/>

  <application
    android:allowBackup="false"
    android:name="com.commonsware.android.stetho.App"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.Apptheme">
    <activity
      android:name="com.commonsware.android.stetho.MainActivity"
      android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
  </application>

</manifest>

This causes Android to create an instance of App, instead of the standard Application class, as the process-wide singleton.

The Debug Application

Over in the debug source set, we have a StethoApp class that extends the App class from the main source set:

package com.commonsware.android.stetho;

import com.facebook.stetho.Stetho;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import okhttp3.OkHttpClient;

public class StethoApp extends App {
  @Override
  public void onCreate() {
    super.onCreate();

    Stetho.initializeWithDefaults(this);
  }

  @Override
  protected OkHttpClient.Builder buildOkBuilder() {
    return(super.buildOkBuilder().addNetworkInterceptor(new StethoInterceptor()));
  }
}

debug source set classes can “see” those in main, which is why we can successfully subclass App.

Here, in onCreate(), we initialize Stetho with a default configuration, using initializeWithDefaults(). If all we wanted was basic Stetho integration, without network call tracking, this would be all that we need.

To integrate network tracking, you need some additional code, based on the particular HTTP client API that you are using. We pulled in the stetho-okhttp3 dependency and are using OkHttp3, so we need to add an OkHttp network interceptor. Such an interceptor is called on each network request, for cross-cutting concerns like logging.

So, we override buildOkBuilder(), call addNetworkInterceptor() to add a StethoInterceptor to the interceptor chain, and return the modified OkHttpClient.Builder. Now, when App uses buildOkBuilder(), it will pull in our subclass override… if the Application singleton for our process is a StethoApp, instead of an App.

Overriding the Application

That requires us to teach a manifest to use StethoApp, in the same way that we modified the main source set’s manifest to use App. The debug source set can have its own manifest, and that manifest can override certain settings from main:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  package="com.commonsware.android.stetho"
  android:versionCode="1"
  android:versionName="1.0">

  <application
    android:name="com.commonsware.android.stetho.StethoApp"
    tools:replace="android:name">
  </application>

</manifest>

Here we tell Android to use StethoApp for the singleton (via the same android:name attribute on the <application> element). And, via tools:replace, we tell the build tools to use our definition for android:name. Otherwise, the build will fail, as there is a conflict between what the main manifest has (App) and what the debug manifest has (StethoApp).

The “Merged Manifest” tab in Android Studio shows that our resulting android:name attribute for a debug build uses StethoApp:

Merged Manifest for Debug Build, Showing StethoApp
Figure 956: Merged Manifest for Debug Build, Showing StethoApp

If we use Android Studio’s Build Variants view to switch to the release build, now the merged manifest shows that the regular App class will be used:

Merged Manifest for Release Build, Showing No StethoApp
Figure 957: Merged Manifest for Release Build, Showing No StethoApp

So, we have achieved our objective: use Stetho in debug builds but not in release builds.

So, with all that, let’s see what Stetho actually gives us.

Connecting Chrome to Your App

The preview of this section will not appear here for a while, due to a time machine mishap.

What You Get In Chrome Dev Tools

The preview of this section is [REDACTED].

Getting Help with Stetho

The preview of this section is sleeping in.