You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java开发:如何打印传入Runnable实例中的代码内容?

How to Print the Code Inside a Passed Runnable in Java

Great question! Let's tackle this step by step. The core issue here is that when you use a Lambda expression (like () -> { /* your code */ }) to create a Runnable, calling toString() on it only gives you a synthetic class identifier (something like YourClass$$Lambda$1/0x0000000800060840@123456), not the actual code inside the run() method. Even with anonymous inner classes, you won't get the raw code directly from toString().

Here are a few practical solutions tailored to your use case:

1. Custom Debug Runnable Wrapper (Simplest & Most Reliable)

Create a wrapper class around Runnable that lets you attach a human-readable description of the code being executed. This is perfect for debugging and requires no extra dependencies.

First, define the wrapper:

public class DebugRunnable implements Runnable {
    private final Runnable delegate;
    private final String codeDescription;

    public DebugRunnable(Runnable delegate, String codeDescription) {
        this.delegate = delegate;
        this.codeDescription = codeDescription;
    }

    @Override
    public void run() {
        // Delegate to the actual runnable logic
        delegate.run();
    }

    @Override
    public String toString() {
        // Return the custom description instead of the default class string
        return codeDescription;
    }
}

Then use it when calling your utility function:

// Pass your Runnable wrapped in DebugRunnable with a code description
sleepAndRunOnUiThread(1000, 
    new DebugRunnable(() -> {
        // Your actual UI thread code
        textView.setText("Updated after delay!");
        progressBar.setVisibility(View.GONE);
    }, 
    "textView.setText(\"Updated after delay!\"); progressBar.setVisibility(View.GONE);"), 
    activity);

Now your existing Log.e(TAG, "sleepAndRunOnUiThread: recieved Runnable action="+action.toString()); line will print the exact code description you provided.

2. Bytecode Analysis (For Deep Debugging)

If you want to automatically extract code details without manual descriptions, you can use bytecode manipulation libraries like ASM to parse the run() method's instructions at runtime. This is more complex but useful for advanced debugging.

First, add the ASM dependency (e.g., for Maven: org.ow2.asm:asm:9.5), then use this helper class:

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.IOException;
import java.io.InputStream;

public class RunnableCodeInspector {
    public static String getRunMethodInstructions(Runnable runnable) throws IOException {
        Class<?> runnableClass = runnable.getClass();
        InputStream classStream = runnableClass.getClassLoader()
            .getResourceAsStream(runnableClass.getName().replace('.', '/') + ".class");
        
        if (classStream == null) return "Could not load class bytes";

        StringBuilder instructionLog = new StringBuilder();
        ClassReader classReader = new ClassReader(classStream);
        
        classReader.accept(new ClassVisitor(Opcodes.ASM9) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                // Target the run() method with no arguments and void return type
                if ("run".equals(name) && "()V".equals(desc)) {
                    return new MethodVisitor(Opcodes.ASM9) {
                        @Override
                        public void visitMethodInsn(int opcode, String owner, String methodName, String methodDesc, boolean isInterface) {
                            String callType = switch(opcode) {
                                case Opcodes.INVOKEVIRTUAL -> "Instance method call";
                                case Opcodes.INVOKESTATIC -> "Static method call";
                                case Opcodes.INVOKESPECIAL -> "Constructor/super method call";
                                default -> "Unknown method call";
                            };
                            instructionLog.append(String.format("%s: %s.%s%s%n", callType, owner, methodName, methodDesc));
                        }

                        @Override
                        public void visitFieldInsn(int opcode, String owner, String fieldName, String fieldDesc) {
                            String fieldAction = opcode == Opcodes.GETFIELD ? "Read field" : "Write field";
                            instructionLog.append(String.format("%s: %s.%s%n", fieldAction, owner, fieldName));
                        }
                    };
                }
                return super.visitMethod(access, name, desc, signature, exceptions);
            }
        }, ClassReader.SKIP_DEBUG);

        return instructionLog.length() > 0 ? instructionLog.toString() : "No instructions found in run() method";
    }
}

Update your utility function to use this inspector:

public static void sleepAndRunOnUiThread(int millis,@NonNull Runnable action,@NonNull Activity activity) { 
    Log.e(TAG, "sleepAndRunOnUiThread: recieved millis="+millis); 
    try {
        String runInstructions = RunnableCodeInspector.getRunMethodInstructions(action);
        Log.e(TAG, "sleepAndRunOnUiThread: Runnable action instructions:\n" + runInstructions);
    } catch (IOException e) {
        Log.e(TAG, "Failed to inspect Runnable code", e);
    }
    new Thread(() -> { 
        try { 
            Thread.sleep(millis); 
            activity.runOnUiThread(action); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }).start(); 
}

This will print low-level instructions (like method calls and field accesses) from the run() method, which can help you understand what the Runnable does without manual descriptions.

3. Anonymous Inner Classes + Debug Metadata (Limited Use)

If you avoid Lambdas and use anonymous inner classes, you can get the class name and method metadata (assuming your code was compiled with debug symbols enabled, using the -g flag). This won't give you raw code, but it can point you to the right place in your source files:

public static String getRunnableDebugInfo(Runnable runnable) {
    Class<?> clazz = runnable.getClass();
    try {
        return String.format("Runnable class: %s (anonymous inner class)", clazz.getName());
    } catch (Exception e) {
        return "Could not retrieve debug info";
    }
}

Call this in your utility function, and the class name (like YourActivity$1) will correspond to the anonymous inner class in your source code, which you can look up manually.


Final Recommendation: For most debugging scenarios, the custom DebugRunnable wrapper is the best choice—it's simple, lightweight, and gives you full control over what gets logged. The bytecode approach is great for deeper analysis but adds complexity.

内容的提问来源于stack exchange,提问作者ansh sachdeva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:49:08