From 565067aa124636e043bd91b21b22a2709ea3882d Mon Sep 17 00:00:00 2001
From: Nullptr
Date: Mon, 29 Jun 2026 23:23:39 +0200
Subject: [PATCH 1/3] feat: object scan
---
README.md | 6 +--
api/build.gradle.kts | 2 +-
.../github/libxposed/api/XposedInterface.java | 49 ++++++++++++++++++-
.../libxposed/api/XposedInterfaceWrapper.java | 15 ++++++
.../io/github/libxposed/api/package-info.java | 9 ++++
5 files changed, 76 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 081f36b..b3009fc 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# libxposed API
-[](https://github.com/libxposed/api)
+[](https://github.com/libxposed/api)
[](https://central.sonatype.com/artifact/io.github.libxposed/api)
[](https://developer.android.com/about/versions/oreo)
[](LICENSE)
@@ -13,7 +13,7 @@ Modern Xposed Module API — a type-safe, redesigned replacement for the legacy
```kotlin
dependencies {
- compileOnly("io.github.libxposed:api:102.0.0")
+ compileOnly("io.github.libxposed:api:103.0.0")
}
```
@@ -33,7 +33,7 @@ These rules keep module entry classes from being removed and rewrite `META-INF/x
```kotlin
dependencies {
- implementation("io.github.libxposed:api:102.0.0")
+ implementation("io.github.libxposed:api:103.0.0")
}
```
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index 8f9ea18..79b4893 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -32,7 +32,7 @@ android {
}
}
-val libVersion = "102.0.0"
+val libVersion = "103.0.0"
val publishSnapshot = providers.gradleProperty("publishSnapshot").orNull == "true"
val dependencySnapshot = providers.gradleProperty("dependencySnapshot").orNull == "true"
fun String.real(snapshot: Boolean) = if (snapshot) "$this-SNAPSHOT" else this
diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
index c36b775..ffe52dd 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
@@ -51,11 +51,20 @@ public interface XposedInterface {
*/
int API_102 = 102;
+ /**
+ * API version 103.
+ * New features
+ *
+ * - Added interfaces for finding already-created objects by class.
+ *
+ */
+ int API_103 = 103;
+
/**
* The API version of this library. This is a static value for the framework.
* Modules should use {@link #getApiVersion()} to check the API version at runtime.
*/
- int LIB_API = API_102;
+ int LIB_API = API_103;
/**
* The framework has the capability to hook system_server and other system processes.
@@ -507,6 +516,44 @@ default int getApiVersion() {
@NonNull
CtorInvoker getInvoker(@NonNull Constructor constructor);
+ /**
+ * Scans the Java heap for live objects matching the supplied class.
+ *
+ * This can be used to obtain already-created target objects directly instead of installing nested hooks
+ * only to capture them from later calls.
+ *
+ * Returned object references are strong references. Keeping the returned results reachable also keeps
+ * returned objects reachable.
+ *
+ * The framework does not run a garbage collection before scanning. Non-reachable but not-yet-collected objects
+ * will be returned as well. If you need to exclude objects that are only waiting to be collected, you should trigger
+ * garbage collection before calling this method.
+ *
+ * The overload of scanning one class is the same as scanning multiple classes with the batched API.
+ * If you need to scan multiple classes, it is recommended to use the batched API to reduce the number
+ * of heap walks and improve performance.
+ *
+ * @param clazz The class to match. Primitive classes and {@code void.class} are not valid.
+ * @param assignable Whether to match subclasses of the class.
+ * @param The expected type of matching objects
+ * @return A list of matching objects. The list is immutable and contains strong references to the objects.
+ */
+ @SinceApi(API_103)
+ @NonNull
+ List findInstances(@NonNull Class clazz, boolean assignable);
+
+ /**
+ * Scans the Java heap for live objects matching the supplied classes.
+ *
+ * @param classes The classes to match. Primitive classes and {@code void.class} are not valid.
+ * @param assignable Whether to match subclasses of the classes.
+ * @return An array of lists of matching objects. The array is in the same order as the input classes.
+ * @see #findInstances(Class, boolean)
+ */
+ @SinceApi(API_103)
+ @NonNull
+ List>[] findInstances(@NonNull Class>[] classes, boolean assignable);
+
/**
* Writes a message to the Xposed log.
*
diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
index fdfc9ae..9b70e9b 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
@@ -11,6 +11,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
+import java.util.List;
import io.github.libxposed.annotation.InternalApi;
import io.github.libxposed.annotation.SinceApi;
@@ -150,6 +151,20 @@ public final CtorInvoker getInvoker(@NonNull Constructor constructor)
return mBase.getInvoker(constructor);
}
+ @NonNull
+ @Override
+ public List findInstances(@NonNull Class clazz, boolean assignable) {
+ ensureAttached();
+ return mBase.findInstances(clazz, assignable);
+ }
+
+ @NonNull
+ @Override
+ public List>[] findInstances(@NonNull Class>[] classes, boolean assignable) {
+ ensureAttached();
+ return mBase.findInstances(classes, assignable);
+ }
+
@Override
public final void log(int priority, @Nullable String tag, @NonNull String msg) {
ensureAttached();
diff --git a/api/src/main/java/io/github/libxposed/api/package-info.java b/api/src/main/java/io/github/libxposed/api/package-info.java
index 74a07ca..4bd4036 100644
--- a/api/src/main/java/io/github/libxposed/api/package-info.java
+++ b/api/src/main/java/io/github/libxposed/api/package-info.java
@@ -97,6 +97,15 @@
* getInvoker(Constructor)}. The invoker type controls what part of the hook chain is executed
* (see {@link io.github.libxposed.api.XposedInterface.Invoker.Type Invoker.Type}).
*
+ * Object Scanning (API 103+)
+ *
+ * Find already-created objects by class with
+ * {@link io.github.libxposed.api.XposedInterface#findInstances(java.lang.Class, boolean)} or
+ * {@link io.github.libxposed.api.XposedInterface#findInstances(java.lang.Class[], boolean)}.
+ * This is useful when a module needs a target object that may already exist before its hooks are
+ * installed, avoiding extra nested hooks whose only purpose is to capture that object from later
+ * calls.
+ *
* Module Lifecycle Callbacks
*
* Override the following callbacks in {@link io.github.libxposed.api.XposedModule}:
From 9673fcd6fc5cb73ace8b4f4e019bdeace1a669f9 Mon Sep 17 00:00:00 2001
From: Nullptr
Date: Mon, 29 Jun 2026 23:37:09 +0200
Subject: [PATCH 2/3] docs: add throws for findInstances
---
api/src/main/java/io/github/libxposed/api/XposedInterface.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
index ffe52dd..7f440e8 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
@@ -537,6 +537,7 @@ default int getApiVersion() {
* @param assignable Whether to match subclasses of the class.
* @param The expected type of matching objects
* @return A list of matching objects. The list is immutable and contains strong references to the objects.
+ * @throws IllegalArgumentException if the class is primitive or void
*/
@SinceApi(API_103)
@NonNull
@@ -548,6 +549,7 @@ default int getApiVersion() {
* @param classes The classes to match. Primitive classes and {@code void.class} are not valid.
* @param assignable Whether to match subclasses of the classes.
* @return An array of lists of matching objects. The array is in the same order as the input classes.
+ * @throws IllegalArgumentException if any class is primitive, void or null
* @see #findInstances(Class, boolean)
*/
@SinceApi(API_103)
From 80d977267d1f31d9554de1d9620ddd6ac638883b Mon Sep 17 00:00:00 2001
From: Nullptr
Date: Wed, 8 Jul 2026 21:19:32 +0200
Subject: [PATCH 3/3] refactor: use array for findInstances
---
.../java/io/github/libxposed/api/XposedInterface.java | 9 +++++----
.../io/github/libxposed/api/XposedInterfaceWrapper.java | 5 ++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
index 7f440e8..4a15c76 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
@@ -536,25 +536,26 @@ default int getApiVersion() {
* @param clazz The class to match. Primitive classes and {@code void.class} are not valid.
* @param assignable Whether to match subclasses of the class.
* @param The expected type of matching objects
- * @return A list of matching objects. The list is immutable and contains strong references to the objects.
+ * @return An array of matching objects. The array is empty if no matching objects are found.
* @throws IllegalArgumentException if the class is primitive or void
*/
@SinceApi(API_103)
@NonNull
- List findInstances(@NonNull Class clazz, boolean assignable);
+ T[] findInstances(@NonNull Class clazz, boolean assignable);
/**
* Scans the Java heap for live objects matching the supplied classes.
*
* @param classes The classes to match. Primitive classes and {@code void.class} are not valid.
* @param assignable Whether to match subclasses of the classes.
- * @return An array of lists of matching objects. The array is in the same order as the input classes.
+ * @return An array of arrays of matching objects. Each inner array corresponds to the matching objects for the class
+ * at the same index in the input array. The inner arrays are empty if no matching objects are found for that class.
* @throws IllegalArgumentException if any class is primitive, void or null
* @see #findInstances(Class, boolean)
*/
@SinceApi(API_103)
@NonNull
- List>[] findInstances(@NonNull Class>[] classes, boolean assignable);
+ Object[][] findInstances(@NonNull Class>[] classes, boolean assignable);
/**
* Writes a message to the Xposed log.
diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
index 9b70e9b..2d0085e 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
@@ -11,7 +11,6 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
-import java.util.List;
import io.github.libxposed.annotation.InternalApi;
import io.github.libxposed.annotation.SinceApi;
@@ -153,14 +152,14 @@ public final CtorInvoker getInvoker(@NonNull Constructor constructor)
@NonNull
@Override
- public List findInstances(@NonNull Class clazz, boolean assignable) {
+ public T[] findInstances(@NonNull Class clazz, boolean assignable) {
ensureAttached();
return mBase.findInstances(clazz, assignable);
}
@NonNull
@Override
- public List>[] findInstances(@NonNull Class>[] classes, boolean assignable) {
+ public Object[][] findInstances(@NonNull Class>[] classes, boolean assignable) {
ensureAttached();
return mBase.findInstances(classes, assignable);
}