Skip to content

RFC for final stable API#51

Open
Dr-TSNG wants to merge 51 commits intomasterfrom
rfc
Open

RFC for final stable API#51
Dr-TSNG wants to merge 51 commits intomasterfrom
rfc

Conversation

@Dr-TSNG
Copy link
Contributor

@Dr-TSNG Dr-TSNG commented Feb 20, 2026

After the merge, the release will be prepared for MavenCentral, and no further breaking changes are permitted.

Key changes

API Version

  • The API constant (value 100) has been removed. Use the new getApiVersion() method instead, which returns 101 for now.

Hook Model — Complete Redesign

The hook model has been completely redesigned from an AOP-style (before/after callbacks) to an OkHttp-style interceptor chain.

Old Model (Removed)

  • BeforeHookCallback and AfterHookCallback interfaces — removed entirely.
  • Hooker was a marker interface; modules provided static before(BeforeHookCallback) and after(AfterHookCallback) methods.

New Model

Modules now implement typed hooker interfaces with an intercept() method:

  • Hooker<T>Object intercept(Chain<T> chain) throws Throwable

The chain objects provide access to the method, this pointer, arguments, and a proceed() method to continue the chain — replacing the old before/after split.

Hooking Methods Now Return Builders

Old New
hook(Method, Class<? extends Hooker>)MethodUnhooker<Method> hook(Method)HookBuilder<Method>
hook(Method, int, Class<? extends Hooker>) Priority set via HookBuilder.setPriority(int)
hook(Constructor<T>, ...)MethodUnhooker<Constructor<T>> hook(Constructor<T>)HookBuilder<Constructor<T>>
hookClassInitializer(Class<T>, ...)MethodUnhooker<Constructor<T>> hookClassInitializer(Class<?>)HookBuilder<Method>

All overloads that accepted priority as a separate parameter have been removed. Priority is now configured through the builder.

MethodUnhooker → HookHandle

  • MethodUnhooker<T> has been removed.
  • Replaced by HookHandle<T>.
  • getOrigin() renamed to getExecutable().
  • unhook() is now explicitly idempotent (safe to call multiple times).

Invoking Original / Special Methods

All direct invoke methods on XposedInterface have been removed:

  • invokeOrigin(Method, ...) — removed
  • invokeOrigin(Constructor<T>, ...) — removed
  • invokeSpecial(Method, ...) — removed
  • invokeSpecial(Constructor<T>, ...) — removed
  • newInstanceOrigin(Constructor<T>, ...) — removed
  • newInstanceSpecial(Constructor<T>, Class<U>, ...) — removed

They are replaced by an Invoker system, obtained through two new methods:

  • getInvoker(Method)Invoker<?, Method>
  • getInvoker(Constructor<T>)CtorInvoker<T>

The invoker's behavior is controlled by an Invoker.Type:

  • Type.ORIGIN — calls the original method, skipping all hooks.
  • Type.Chain(maxPriority) — calls through the hook chain starting from a specific priority.
  • Type.Chain.FULL — calls with the full hook chain.

invokeSpecial() and newInstanceSpecial() are now methods on the invoker objects themselves.

Deoptimize

The two overloads deoptimize(Method) and deoptimize(Constructor<T>) have been merged into a single method:

boolean deoptimize(Executable executable);

Framework Privilege → Properties

getFrameworkPrivilege() and its associated constants have been removed:

  • FRAMEWORK_PRIVILEGE_ROOT — removed
  • FRAMEWORK_PRIVILEGE_CONTAINER — removed
  • FRAMEWORK_PRIVILEGE_APP — removed
  • FRAMEWORK_PRIVILEGE_EMBEDDED — removed

Replaced by getFrameworkProperties() which returns a bitmask:

Capability Value Meaning
PROP_CAP_SYSTEM 1L The framework has the capability to hook system_server and other system processes
PROP_CAP_REMOTE 1L << 1 The framework provides remote preferences and remote files support
PROP_RT_API_PROTECTION 1L << 2 The framework disallows accessing Xposed API via reflection or dynamically loaded code

Priority Constants

Constant Old Value New Value
PRIORITY_LOWEST -10000 Integer.MIN_VALUE
PRIORITY_HIGHEST 10000 Integer.MAX_VALUE

Logging

  • log(String) and log(String, Throwable) have been removed (were already @Deprecated).
  • A new overload without Throwable has been added: log(int priority, String tag, String msg).

DexParser

The DexParser utility class and parseDex(ByteBuffer, boolean) method have been removed entirely. Consider using DexKit as an alternative.

Module Lifecycle

Constructor Change

XposedModule no longer receives XposedInterface and ModuleLoadedParam in its constructor. The framework now calls attachFramework(XposedInterface) on the wrapper automatically. Modules should not perform initialization before onModuleLoaded() is called.

Callback Renames and Splits

Old New
onSystemServerLoaded(SystemServerLoadedParam) onSystemServerStarting(SystemServerStartingParam)
onPackageLoaded(PackageLoadedParam) Split into two callbacks (see below)

The package loading lifecycle is now split into two phases:

  1. onPackageLoaded(PackageLoadedParam) — Called when the default classloader is ready, before AppComponentFactory instantiation. Requires API 29+.
  2. onPackageReady(PackageReadyParam) — Called after AppComponentFactory has created the app classloader. Provides getClassLoader() and getAppComponentFactory().

Note: getClassLoader() has been moved from PackageLoadedParam to PackageReadyParam.

Copilot AI review requested due to automatic review settings February 20, 2026 20:35
@Dr-TSNG Dr-TSNG mentioned this pull request Feb 20, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refines the public LibXposed API toward a “final stable” shape by introducing an explicit module-load callback, restructuring hook handle types around Executable, and raising the Android minSdk accordingly.

Changes:

  • Add onModuleLoaded(ModuleLoadedParam) callback to separate “module attached” from instantiation timing.
  • Redesign hooking APIs: replace Member/MethodUnhooker with Executable-based generics and new hook handle types.
  • Raise api module minSdk from 24 → 26 to support java.lang.reflect.Executable in the public surface.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java Adds onModuleLoaded callback and related Javadoc.
api/src/main/java/io/github/libxposed/api/XposedModule.java Removes constructor-based initialization and updates guidance to use onModuleLoaded.
api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java Switches to late attachment (attachFramework) and updates wrapper methods to new hook APIs.
api/src/main/java/io/github/libxposed/api/XposedInterface.java Updates core API types to Executable, introduces hook handle interfaces, and adjusts invocation/deoptimize APIs.
api/build.gradle.kts Bumps minSdk to 26 to match new Executable usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@libxposed libxposed deleted a comment from hsnabod175 Feb 21, 2026
@DavidBerdik
Copy link

I see that #15 has been closed but it does not look to me like it was implemented in this pull request. Have the plans to implement it been scrapped?

The side effects of individual hooker class are too severe while introducing minimum advantage. Because dsl and lambda becomes impossible, every module would implement its own hook dispatchers, making cooperation impossible and destroy hook dump readability.
@programminghoch10
Copy link

Hi,
where are the resource and layout hooks defined that were present on the previous Xposed API (93)?
For UI modification modules these hooks are absolutely mandatory and would force many great modules to completely rewrite functionality using much more unstable/unreliable code hooks.
Having a centralized proper framework implementation of resource and layout hooks, like the previous API had, would be essential for these kind of modules.

@Dr-TSNG
Copy link
Contributor Author

Dr-TSNG commented Feb 21, 2026

Hi, where are the resource and layout hooks defined that were present on the previous Xposed API (93)? For UI modification modules these hooks are absolutely mandatory and would force many great modules to completely rewrite functionality using much more unstable/unreliable code hooks. Having a centralized proper framework implementation of resource and layout hooks, like the previous API had, would be essential for these kind of modules.

Resource hooks are removed in new API because of already low compatibility with new Android versions. We may offer a more modern resource hook solution in the future, but it is not currently part of the API 100 initiative.

@binarynoise
Copy link

Some time ago, when I tried to use the new API, I got confused because some methods are available both on XposedInterface (this) and XposedModuleInterface.PackageLoadedParam (param).
Ideally there would be a version where we opt to implement attachFramework ourselves so we can manually delegate to the base so it is more obvious whether we get info about the module or the target.
Also, mixing XposedInterface and XposedModuleInterface in one object feels kinda against separation of concerns.

@MhmRdd
Copy link

MhmRdd commented Mar 2, 2026

@programminghoch10 Resource hooks have been replaced by SDK API, we will redesign resource hooks based on runtime resource overlay.

FabricatedOverlay is Android 14 based SDK implementation, what about Android 8+ ?

@Dr-TSNG
Copy link
Contributor Author

Dr-TSNG commented Mar 2, 2026

Some time ago, when I tried to use the new API, I got confused because some methods are available both on XposedInterface (this) and XposedModuleInterface.PackageLoadedParam (param). Ideally there would be a version where we opt to implement attachFramework ourselves so we can manually delegate to the base so it is more obvious whether we get info about the module or the target. Also, mixing XposedInterface and XposedModuleInterface in one object feels kinda against separation of concerns.

XposedInterface is implemented by the framework. XposedInterfaceWrapper is used to delegate the implementation to the framework interface. XposedModuleInterface is override by modules. So a Module extends XposedInterfaceWrapper and implements XposedModuleInterface. You shouldn't override the wrapper interface because it already did the delegation. For the same name methods, I suggest you mean getApplicationInfo. XposedInterface.getApplicationInfo returns info about the module while PackageLoadedParam.getApplicationInfo returns info about the package being loaded.

@binarynoise
Copy link

I later figured out which getApplicationInfo gives me what I want.
Needing to explicitly choose to use the XposedInterface.getApplicationInfo instead of implicitly would prevent confusion for other devs exploring the new API.

Rovo's old API had it cleanly separated: I implement the XposedModule Interface and all the other info would be passed in via objects. This was intuitive. Maybe passing the XposedInterface object via constructor parameter would retain that and would make it easier to pick the right method.

@MhmRdd
Copy link

MhmRdd commented Mar 2, 2026

I later figured out which getApplicationInfo gives me what I want.
Needing to explicitly choose to use the XposedInterface.getApplicationInfo instead of implicitly would prevent confusion for other devs exploring the new API.

Rovo's old API had it cleanly separated: I implement the XposedModule Interface and all the other info would be passed in via objects. This was intuitive. Maybe passing the XposedInterface object via constructor parameter would retain that and would make it easier to pick the right method.

I think it's better to stick with the new one, saying getApplicationInfo without XposedInterface seems generic and doesn't differentiate between the module & package loaded.

@xutianyang121
Copy link

11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants