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:

There is a bug in Stetho 1.4.2,
where they forgot a particular dependency on part of the Android Support
libraries

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:

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:

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:

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.

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>:

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:

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).