Network debugging in android with Stetho (OkHttp, Retrofit, Glide) + Separating build variants

Stetho

Stetho is a debug bridge for Android, created by Facebook. Helps you use your Chrome inspector to inspect your app’s Network, DB, View Hierarchy, etc. If you’ve ever used the Chrome inspector before, You know that tool is very useful for network debugging.

These are Stetho’s specifications.

image_1

image_2

image_3

This post will be about integrating with Android libraries related to Networking (OkHttp - Retrofit, Glide).

TL;DR

  • Integrating with Stetho
    • Add dependencies & proguard
    • Initialize Stetho and make Stetho injected OkHttpClient & Retrofit
    • Make a Stetho injected glide module to replace a default glide module
    • Using GlideApp
    • Debugging on Chrome inspector
  • Extra part
    • Separate build variants (Build type)

Integrating with Stetho

Add dependencies & proguard

Add dependencies for network debugging. Stetho, Glide, Retrofit.
C.F) DebugImplementation is for separating build variants. This will be explained in extra part.

dependencies {
    //For OkHttp3 & Stetho
    debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'

    //For Retrofit
    Implementation 'com.squareup.retrofit2:retrofit:2.5.0' 

    //For Glide
    Implementation 'com.github.bumptech.glide:glide:4.11.0'
    debugImplementation "com.github.bumptech.glide:okhttp3-integration:4.11.0"

    // Depending on the language you write for AppGlideModule class, add different annotation processor as follows.
    // For kotlin
    kapt 'com.github.bumptech.glide:compiler:4.11.0'
    // For Java
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

If you are using Proguard, Add some required rules for Glide & OkHttp & Retrofit on your proguard-rules.

# GLIDE
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule


# Retrofit
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
# Retain generic type information for use by reflection by converters and adapters.
-keepclassmembernames,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn org.conscrypt.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

Initialize Stetho and make Stetho injected OkHttpClient & Retrofit

Initialize Stetho with an Application context.
C.F) Usually, Stetho is initialized at an Application class.

 Stetho.initializeWithDefaults(context)

Make OkHttpClient which is injected Stetho.

val preparedOkHttpClient = OkHttpClient.Builder().addNetworkInterceptor(StethoInterceptor()).build()

Make a Stetho injected glide module to replace a default glide module

For debugging a glide network, You need replace a default Glide module to a Stetho-injected Glide module.

To replace the default glide module, Create an AppGlideModule for connecting Glide and the Stetho-injected OkHttp, first.

@GlideModule
class OkHttp3GlideModule : AppGlideModule() {
    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        registry.replace(
            GlideUrl::class.java,
            InputStream::class.java,
            OkHttpUrlLoader.Factory(BaseNetworkTools.preparedOkHttpClient)
        )
    }
}

After finishing making GlideModule, Register meta-data at Android manifest in the application tags.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.lakelab.android.network">
    <application>
        <meta-data
            android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
            tools:node="remove" />
    </application>
</manifest>

C.F) This is for avoiding conflict between your glide module and the default glide module. If you want to know the detail about this, You can explore here.

Using GlideApp

Using GlideApp instead of normal Glide.

GlideApp.with(this@MainActivity)
        .load(SAMPLE_IMAGE_ICON)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .skipMemoryCache(true).into(am_sample_image)

This is because limiting the generated API to applications allows us to have a single implementation of the API in Glide. For more details about this, Please visit here.

Debugging on Chrome inspector

Open Chrome and Browse chrome://inspect/#devices for enjoying easy debugging :)

image_1

Extra part

It should be a disaster if your application can be debugged on a release build. So They usually use it this way.

if (BuildConfig.DEBUG) {
     // initialize
}

But this way can make your codes complex and easy to make confused. So For clear and mistake-proof code, Separating builds variants (Build type) will be introduced in this extra part.

Separate build variants (Build type)

It’s already separated by Debug/Release when you make a project. So Only the Separating folder is necessary for separating build variants. This is my sample project structure for separating build variants.

image_5

Make two directories debug/release like above and Make classes of the same name in each directory as follows.

// StethoUtils in debug directory
class StethoUtils {
    companion object {
        @JvmStatic
        internal fun initStetho(context: Context) = Stetho.initializeWithDefaults(context)

        @JvmStatic
        internal fun makeBuilderWithStetho() =
                OkHttpClient.Builder().addNetworkInterceptor(StethoInterceptor())
    }
}

// OkHttp3GlideModule in debug directory
@GlideModule
class OkHttp3GlideModule : AppGlideModule() {
    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        registry.replace(
            GlideUrl::class.java,
            InputStream::class.java,
            OkHttpUrlLoader.Factory(BaseNetworkTools.preparedOkHttpClient)
        )
    }
}
// StethoUtils in release directory
class StethoUtils {
    companion object {
        @JvmStatic
        internal fun initStetho(context: Context) {
        }

        @JvmStatic
        internal fun makeBuilderWithStetho() =
            OkHttpClient.Builder()
    }
}

// OkHttp3GlideModule in release directory
@GlideModule
class OkHttp3GlideModule : AppGlideModule() {
    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
    }
}

Your manifest even can be separated by the debug variant as follows. It will be merged automatically with your manifest in the main directory.

<!--AndroidManifest.xml in debug directory-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.lakelab.android.network">

    <application>
        <meta-data
            android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
            tools:node="remove" />
    </application>
</manifest>

C.F) You can change your build variant in the menu in Android Studio as follows.

image_6

Now, Your code is completely separated by debugging and release build variables, And It makes you safe from mistakenly leaking your network information.

References

If you’d like to see the full codes for this post

Please feel free to visit here.