Mobile Development 13 min read

Deep Dive into ASM for Android Bytecode Instrumentation and OkHttp Monitoring

This article explains how to use ASM to instrument Android bytecode for automatic click event tracking and OkHttp network request monitoring, detailing the Transform API, AOP/ASM techniques, custom ClassVisitor and MethodVisitor implementations, code injection strategies, and provides a complete open‑source example on GitHub.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Deep Dive into ASM for Android Bytecode Instrumentation and OkHttp Monitoring

Project background : Following the QAPM client performance monitoring system, developers needed to add network monitoring for okhttp. The article shares the technologies, simple entry to automatic instrumentation, and challenges encountered when hooking okhttp3 methods.

Related technologies : Android Gradle Plugin Transform API, which allows custom processing of compiled .class files before they are converted to .dex. The article also introduces Aspect‑Oriented Programming (AOP) and the ASM library as the underlying bytecode manipulation mechanism.

Entry practice – using ASM to count View click events :

public class TestClass implements View.OnClickListener {
    View view;
    void test(){
        view.setOnClickListener(this);
    }
    @Override
    public void onClick(View view) {
        System.out.println("我处理View点击事件");
    }
}

To achieve unified click tracking, the original setOnClickListener call is replaced with a custom RecodeOnClickListener wrapper:

public class RecodeOnClickListener implements View.OnClickListener {
    View.OnClickListener listener;
    public RecodeOnClickListener(View.OnClickListener listener){
        this.listener = listener;
    }
    @Override
    public void onClick(View view) {
        if (listener != null) {
            listener.onClick(view);
        }
        System.out.println("我记录了View点击事件");
    }
}

The ASM transformation inserts the creation of RecodeOnClickListener before the original setOnClickListener call:

mv.visitTypeInsn(NEW, "com/mqunar/qapm/okhttp/RecodeOnClickListener");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "com/mqunar/qapm/okhttp/RecodeOnClickListener", "<init>", "(Landroid/view/View$OnClickListener;)V", false);

OkHttp client monitoring : The article shows how to intercept OkHttp requests by replacing new OkHttpClient() (or new OkHttpClient.Builder()) with a custom utility that adds an interceptor:

public static OkHttpClient getOkHttpClient(){
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.addInterceptor(new QOkHttpInterceptor());
    return builder.build();
}

The corresponding ASM change swaps the object creation with a static method call:

mv.visitMethodInsn(INVOKESTATIC, "com/mqunar/qapm/okhttp/OkHttpUtils", "getOkHttpClient", "()Lokhttp3/OkHttpClient;", false);

Hook design : To support more complex scenarios where a single method call expands into multiple instructions, the article proposes a two‑phase approach – first marking the bytecode locations that need hooking, then performing the actual injection. Custom HookInitMethodClassVisitor and HookInitMethodVisitor classes record indices of NEW and DUP instructions and decide whether to skip or replace them.

Key snippets of the custom visitors:

class HookInitMethodClassVisitor extends ClassVisitor {
    // ... constructor stores class name and context
    @Override
    MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor originMv = super.visitMethod(access, name, desc, signature, exceptions);
        if (hook) {
            // find matching method in context and return custom visitor
            return new HookInitMethodVisitor(originMv, matchingMethod);
        }
        return originMv;
    }
}

class HookInitMethodVisitor extends GeneratorAdapter implements Opcodes {
    @Override
    void visitTypeInsn(int opcode, String type) {
        if (opcode == NEW && hook && method.newVisiTypeInsnIndexs.contains(currentIndex)) {
            return; // skip original NEW
        }
        super.visitTypeInsn(opcode, type);
    }
    @Override
    void visitInsn(int opcode) {
        if (opcode == DUP && hook && method.dupVisiInsnIndexs.contains(currentDupIndex)) {
            return; // skip original DUP
        }
        super.visitInsn(opcode);
    }
    @Override
    void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        // replace setOnClickListener or OkHttp constructor with custom logic
        super.visitMethodInsn(...);
    }
}

The full implementation, along with additional network monitoring features, is available in the open‑source GitHub repository linked at the end of the article.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidaopbytecodeASMOkHttp
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.