didi/DoKit · error · ReflectedException

Oops!

Error message

Oops!

What it means

Reflector.on(String, boolean, ClassLoader) wraps Class.forName in a try-catch and rethrows any failure as ReflectedException("Oops!", e). The 'Oops!' message is generic; the real cause (usually ClassNotFoundException, or an initialization failure from the static initializer when initialize=true) is attached as the cause. This entry point is how DoraemonKit reflectively loads framework internals that vary across Android OS versions or vendor ROMs.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/util/Reflector.java:48

        }
        public ReflectedException(String message, Throwable cause) {
            super(message, cause);
        }
    
    }
    public static Reflector on(@NonNull String name) throws ReflectedException {
        return on(name, true, Reflector.class.getClassLoader());
    }
    
    public static Reflector on(@NonNull String name, boolean initialize) throws ReflectedException {
        return on(name, initialize, Reflector.class.getClassLoader());
    }
    
    public static Reflector on(@NonNull String name, boolean initialize, @Nullable ClassLoader loader) throws ReflectedException {
        try {
            return on(Class.forName(name, initialize, loader));
        } catch (Throwable e) {
            throw new ReflectedException("Oops!", e);
        }
    }
    
    public static Reflector on(@NonNull Class<?> type) {
        Reflector reflector = new Reflector();
        reflector.mType = type;
        return reflector;
    }
    
    public static Reflector with(@NonNull Object caller) throws ReflectedException {
        return on(caller.getClass()).bind(caller);
    }
    
    protected Reflector() {
    
    }
    
    public Reflector constructor(@Nullable Class<?>... parameterTypes) throws ReflectedException {

View on GitHub (pinned to 626827cddb)

Solutions

  1. Read the attached cause: ClassNotFoundException means the name is wrong for this OS version — guard the call per Build.VERSION.SDK_INT
  2. Pass initialize=false if the class's static initializer is what fails
  3. Verify the fully-qualified class name against the AOSP source for the device's API level
  4. Switch to QuietReflector.on(...) which swallows the failure and exposes mIgnored instead of throwing
  5. If obfuscation renamed the target, add proguard keep rules for the reflected class

Example fix

// before
Reflector refl = Reflector.on("android.app.ActivityThread");

// after
if (Build.VERSION.SDK_INT >= 29) {
    Reflector refl = Reflector.on("android.app.ActivityThread", false);
} else {
    Reflector.QuietReflector refl = Reflector.QuietReflector.on("android.app.ActivityThread");
    // refl.mIgnored != null -> feature unavailable on this device
}
Defensive patterns

Strategy: try-catch

Validate before calling

public static boolean classExists(String fqcn, ClassLoader cl) {
    try {
        Class.forName(fqcn, false, cl != null ? cl : Reflector.class.getClassLoader());
        return true;
    } catch (Throwable t) {
        return false;
    }
}

Try / catch

try {
    Reflector r = Reflector.on("android.app.ActivityThread");
} catch (ReflectedException e) {
    Throwable cause = e.getCause(); // usually ClassNotFoundException
    if (cause instanceof ClassNotFoundException) { /* feature unavailable on this build */ }
}

Prevention

When it happens

Trigger: Calling Reflector.on("android.some.InternalClass") when the class does not exist on the running device (removed/renamed in that API level), when the class is not visible to the supplied ClassLoader, or when initialize=true and the class's static initializer throws.

Common situations: Running DoraemonKit on an Android version whose hidden framework classes were refactored; vendor ROMs that strip internals; proguard/R8 renaming obfuscating the target class name; wrong ClassLoader in plugin/embedded environments.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/aff7cba7205bac9a. Report an issue: GitHub.