Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Retroflector API Documentation

Retroflector is a compile-time + runtime reflection bridge for hidden Android/framework APIs. You define Java/Kotlin stub interfaces with @Proxy* annotations; KSP generates *Proxy accessors and *Context/*Static proxy interfaces. At runtime, Retroflector turns those interfaces into JDK dynamic proxies backed by real reflection.

Table of Contents


Setup

Add Retroflector as a dependency and enable the KSP processor in the module that owns your stub interfaces:

dependencies {
    implementation(project(":Retroflector"))
    ksp(project(":Retroflector"))
}

Stub interfaces live in your app/module source. Generated Kotlin files land in build/generated/ksp/<variant>/kotlin/.


Retroflector

Retroflector is the main runtime entry point. It builds JDK proxies for generated *Context and *Static interfaces and dispatches calls to hidden fields/methods/constructors via Reflector.

Fields

@JvmField
var DEBUG: Boolean

@JvmField
var CACHE: Boolean
  • DEBUG — when true, reflection failures print stack traces before fallback/throw behavior runs.
  • CACHE — reserved flag (currently unused by proxy cache; static proxies are always cached per interface class).

Methods

create

@JvmStatic
fun <T> create(clazz: Class<T>, caller: Any?, withException: Boolean): T?
  • Description: Creates (or returns cached) proxy for clazz.
  • Parameters:
    • clazz — generated *Context or *Static interface class.
    • caller — instance to bind for instance calls; pass null for static access.
    • withException — when true, reflection errors propagate; when false, returns default/null for reference types (primitives may throw NullPointerException).
  • Returns: Proxy instance, or null if target class cannot be resolved.

Stub Annotations

Annotate stub interfaces in source. KSP reads these at compile time; runtime reads @ProxyClassName / @ProxyClass / generated @ProxyClassNameRef.

Annotation Target Purpose
@ProxyClassName("fqcn") class Map stub to real class by name
@ProxyClass class Map stub to real KClass
@ProxyField function Instance field getter
@ProxyStaticField function Static field getter
@ProxyMethod function Instance method
@ProxyStaticMethod function Static method
@ProxyConstructor function Constructor
@ProxyParamClass parameter Override reflected param type
@ProxyParamClassName parameter Override reflected param type by name

Generated interfaces also use @ProxyFieldRef, @ProxyFieldSet, @ProxyFieldCheck, @ProxyMethodCheck, @ProxyConstructorRef — you do not hand-write these.

Example stub:

@ProxyClassName("android.app.Activity")
public interface Activity {
    @ProxyField ActivityInfo mActivityInfo();
    @ProxyMethod void onActivityResult(int a, int b, Intent data);
}

KSP Generated Code

For each @ProxyClassName / @ProxyClass stub Foo, KSP emits:

File Role
FooProxy.kt Entry object with get(), getWithException(), get(caller), getRealClass()
FooContext.kt Instance fields/methods/constructors
FooStatic.kt Static fields/methods/constructors

FooProxy.get(caller) returns FooContext. FooProxy.get() returns FooStatic. Static field getters are generated as nullable boxed types (Int?, etc.) so Java callers can null-check when a field is missing on a device/API level.


ClassUtil

Resolves the real Class<*> behind a stub or generated interface.

Methods

classReady

@JvmStatic
fun classReady(clazz: Class<*>): Class<*>?
  • Description: Walks @ProxyClassNameRef, @ProxyClass, or @ProxyClassName to load the target class.
  • Returns: Resolved class, or null if not found.

Reflector

Low-level fluent wrapper around java.lang.reflect for direct use outside generated proxies. Also powers Retroflector internally.

Companion Object

@JvmStatic
fun on(type: Class<*>): Reflector

@JvmStatic
@Throws(Exception::class)
fun on(name: String): Reflector

@JvmStatic
@Throws(Exception::class)
fun with(value: Any?): Reflector
  • on(Class) / on(String) — start a chain on a type.
  • with(Any?) — bind an instance caller.

Reflector.QuietReflector exposes the same API but swallows errors and returns null.

Methods

Method Description
field(name) Select field (walks supers)
get() / get(target) Read field
set(value) / set(target, value) Write field
method(name, *paramTypes) Select method
call(*args) / callByCaller(target, *args) Invoke method
constructor(*paramTypes) Select constructor
newInstance(*args) Construct instance
bind(value) / unbind() Set/clear default caller
getField() / getMethod() Expose resolved Field / Method

All non-quiet methods throw Exception on failure.


JavaReflect

Legacy shim that forwards to Retroflector. Use Retroflector in new code.

JavaReflect.create(clazz, caller, withException)
JavaReflect.DEBUG
JavaReflect.CACHE

Usage Example

1. Define stub (src/main/java/miaw/android/app/Activity.java):

@ProxyClassName("android.app.Activity")
public interface Activity {
    @ProxyField ActivityInfo mActivityInfo();
}

2. Use generated bridge (after build):

import miaw.android.app.ActivityProxy

val activity = someActivityInstance
val info = ActivityProxy.get(activity).mActivityInfo()

// static side
val realClass = ActivityProxy.getRealClass()
ActivityInfo info = ActivityProxy.get(activity).mActivityInfo();
Class<?> cls = ActivityProxy.getRealClass();

3. Direct reflection (no stub codegen):

val value = Reflector.on("android.os.SystemProperties")
    .method("get", String::class.java, String::class.java)
    .call<String>("ro.build.version.sdk", "0")

Notes

  • Stub interfaces must use the @Proxy* annotations; KSP only processes annotated members.
  • Pass withException = true (getWithException()) when failures should throw instead of returning null/defaults.
  • Instance proxies hold a weak reference to caller; if the caller is GC'd, instance calls fall back to null/default behavior.
  • Raw Java generic types in stubs (e.g. List without type args) are emitted as star-projected Kotlin types (MutableList<*>).

About

Type-safe Android hidden API reflection powered by KSP and dynamic proxies.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages