Xposed-Modul-Upgrade auf LibXposed API 102 – Vollständiger Leitfaden
Dieses Dokument fasst den Standardprozess, die Code-Paradigmen, Metadaten-Spezifikationen und Leistungsoptimierungsstrategien für die Migration traditioneller Xposed-Module (basierend auf XposedBridge API 54-93) zur modernen LibXposed API 102 (für LSPosed 1.9.3+, Vector und andere moderne Frameworks) zusammen.
Zukünftige Upgrade-Anweisungen für andere Module können direkt auf diese Spezifikation verweisen.
1. Wichtigste Änderungen im Vergleich
| Eigenschaft / Dimension | Traditionelles Xposed (API 54-93) | Modernes LibXposed (API 102) |
|---|---|---|
| Abhängigkeitskoordinaten | de.robv.android.xposed:api:82 (oder 93) | io.github.libxposed:api:102.0.0 |
| Einstiegsklasse/-schnittstelle | IXposedHookLoadPackage, IXposedHookZygoteInit | Erbt von io.github.libxposed.api.XposedModule |
| Einstiegsdeklaration | assets/xposed_init | META-INF/xposed/java_init.list |
| Moduleigenschaften-Konfiguration | <meta-data> in AndroidManifest.xml | META-INF/xposed/module.prop |
| Scope-Deklaration | xposedscope Resource-Array | META-INF/xposed/scope.list |
| Hook-Programmiermodell | XC_MethodHook (before / after) Callbacks | OkGO-ähnliche Interceptor-Kette Hooker (chain.proceed()) |
| Hot Reload | Nicht unterstützt (Neustart der Ziel-App oder des Systems erforderlich) | Native Unterstützung (Überschreiben von onHotReloading / onHotReloaded) |
| Zygote-Injection | Unterstützt initZygote für globalen Zugriff | Zygote-Injection verboten; nur in Zielprozesse des Scopes |
| Obfuscation/R8-Unterstützung | Strikte Beibehaltung von Hook-Methoden- und Klassennamen erforderlich | Native Unterstützung; Deklaration von Anpassungsregeln ausreichend |
2. Standard-Migrationsverfahren in vier Schritten
Schritt 1: Build & Abhängigkeiten migrieren (build.gradle)
-
Abhängigkeiten aktualisieren: Ersetzen Sie die Abhängigkeit in der
build.gradledes Moduls (z. B.app/build.gradleoderhook/build.gradle):dependencies { // Entfernen: compileOnly 'de.robv.android.xposed:api:82' // Einführen der modernen LibXposed 102 API compileOnly 'io.github.libxposed:api:102.0.0' compileOnly 'androidx.annotation:annotation:1.5.0' } -
Java-Kompilierungskompatibilität:
android { compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } } -
ProGuard / R8-Regeln konfigurieren (
proguard-rules.pro):-dontwarn io.github.libxposed.annotation.** -adaptresourcefilecontents META-INF/xposed/java_init.list -keep,allowoptimization,allowobfuscation public class * extends io.github.libxposed.api.XposedModule { public <init>(); }
Schritt 2: Moderne Metadaten-Dateien konfigurieren (META-INF/xposed/)
Erstellen Sie die folgenden Dateien im Modul-Quellverzeichnis src/main/resources/META-INF/xposed/:
1. module.prop (Modul-Metadaten)
minApiVersion=102
targetApiVersion=102
autoHotReload=true
exceptionMode=protective
minApiVersion: Mindestens unterstützte API-Version, auf102setzen.targetApiVersion: Ziel-API-Version, auf102setzen.autoHotReload: Auftruesetzen, um beim Installieren eines Modul-Updates automatisch ein Hot Reload auszulösen.exceptionMode:protective(Standard-Schutzmodus; Hook-Ausnahmen stürzen die Host-App nicht ab) oderpassthrough.
2. java_init.list (Einstiegsdeklaration)
Einzeilige Deklaration des vollqualifizierten Klassennamens des Modul-Einstiegs:
com.example.myhook.MainHook
3. scope.list (Empfohlener Scope)
Ein Zielpaketname pro Zeile, der die Standard-Applikationen deklariert, in die das Modul injected werden soll:
com.android.chrome
com.microsoft.emmx
com.example.targetapp
Schritt 3: Manifest-Datei konfigurieren (AndroidManifest.xml)
Aktualisieren Sie die AndroidManifest.xml des Moduls:
<application ...>
<!-- Traditionelle Metadaten für Kompatibilität beibehalten; wichtig: xposedminversion auf 102 setzen -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Modul-Funktionsbeschreibung" />
<meta-data
android:name="xposedminversion"
android:value="102" />
<meta-data
android:name="xposedscope"
android:resource="@array/xposed_scope" />
</application>
Schritt 4: Hook-Logik umschreiben (XposedModule)
1. Basis-Template und Lebenszyklus
package com.example.myhook;
import android.util.Log;
import androidx.annotation.NonNull;
import io.github.libxposed.api.XposedInterface;
import io.github.libxposed.api.XposedModule;
public class MainHook extends XposedModule {
private static final String TAG = "MyHookModule";
private volatile boolean mHooked = false;
public MainHook() {
super();
}
/**
* Wird ausgelöst, wenn der ClassLoader der Host-App bereit ist und kurz vor der Erstellung der Application ist
*/
@Override
public void onPackageReady(@NonNull PackageReadyParam param) {
if (mHooked) return;
synchronized (this) {
if (mHooked) return;
mHooked = true;
initHooks(param.getPackageName(), param.getClassLoader());
}
}
/**
* Hot-Reload-Vorbereitungs-Callback (wird in der alten Version des Codes ausgeführt)
* Rückgabe true erlaubt dem Framework, die neue Version zu ersetzen
*/
@Override
public boolean onHotReloading(@NonNull HotReloadingParam param) {
return true;
}
/**
* Hot-Reload-Abschluss-Callback (wird in der neuen Version des Codes ausgeführt)
*/
@Override
public void onHotReloaded(@NonNull HotReloadedParam param) {
// Alle in der alten Version registrierten Hooks entfernen
param.getOldHookHandles().forEach(XposedInterface.HookHandle::unhook);
synchronized (this) {
mHooked = false;
initHooks("hot-reload", getClass().getClassLoader());
}
}
private void initHooks(String packageName, ClassLoader classLoader) {
// Führen Sie hier die spezifische Hook-Logik aus
}
}
2. Wichtige Hook-Syntax im Vergleich
| Zieloperation | Traditionelle Xposed-Schreibweise | Moderne LibXposed 102-Schreibweise |
|---|---|---|
| Methode finden und Hooken | XposedHelpers.findAndHookMethod("Klassenname", cl, "methode", argTypes..., callback) | Method m = Clazz.class.getDeclaredMethod("methode", argTypes...);hook(m).intercept(chain -> { ... }); |
| Originalmethode ausführen | Standardmäßig direkt ausgeführt; Aufruf von param.getResult() | Object result = chain.proceed(); |
| Parameter ändern und aufrufen | param.args[0] = newVal; | Object result = chain.proceed(new Object[]{ newVal, ... }); |
| Rückgabewert abfangen und ersetzen | param.setResult(customVal); | Direkt benutzerdefinierten Wert zurückgeben (ohne chain.proceed() aufzurufen):hook(m).intercept(chain -> customVal); |
| Eingabeparameter abrufen | param.args[i] | chain.getArg(i) oder chain.getArgs() |
| This-Instanz abrufen | param.thisObject | chain.getThisObject() |
| Konstruktor hooken | findAndHookConstructor(...) | Constructor<?> ctor = ...;hook(ctor).intercept(chain -> ...); |
| Statischen Block hooken | Traditionell kein Standard-API | hookClassInitializer(TargetClass.class).intercept(...) |
3. Hochfrequente Hook-Szenarien und tiefgehende Leistungsoptimierungsmuster
Die goldene Optimierungsregel für hochfrequente Aufrufe (z. B. isEnabled, View-Zeichnen, Touch-Weiterleitung):
-
Schneller Null-Overhead-Pfad (Sondierungs-Durchgriff): Wenn es sich um eine Zustandsabfragemethode handelt (z. B.
isEnabled(),isLoaded()), rufen Sie zunächst die leichtgewichtige native Feldlesungchain.proceed()auf. Wenn der tatsächliche Zustand bereits falsch ist, geben Sie direkt false zurück und überspringen die gesamte nachfolgende Logik und Stack-Überprüfungen. -
Android 14+ (API 34+) – Verwendung von
StackWalkerfür lazy Traversierung: Herkömmlichesnew Throwable().getStackTrace()führt zu einer vollständigen Thread-Stack-Entfaltung (Native Unwind) und Hunderten von String-Allokationen, was leicht zu GC-Drops führt. Verwenden Siejava.lang.StackWalkeraus Java 9 / Android 14+, für eine bedarfsgesteuerte, schrittweise Abtastung ohne vollständige Stack-Allokation. -
Dynamische Stack-Positionierung unter Android < 34 (keine fest codierten Indizes): Führen Sie nur einmalig
new Throwable().getStackTrace()aus, durchlaufen Sie ihn nach unten, um die Systemklasse zu finden, die die Zielmethode enthält (z. B.AccessibilityManager). Der unmittelbar folgende Stackframe ist der tatsächliche Aufrufer. Niemals[3]oder[4]fest codieren, um einen stabilen Betrieb über verschiedene LSPosed-Versionen hinweg zu gewährleisten.
Beispielcode für Leistungsoptimierung:
// Prüfung auf Android 14+ StackWalker
private static final boolean HAS_STACK_WALKER;
static {
boolean has;
try {
Class.forName("java.lang.StackWalker");
has = (Build.VERSION.SDK_INT >= 34);
} catch (Throwable t) {
has = false;
}
HAS_STACK_WALKER = has;
}
// Beispiel für einen hochfrequenten Methoden-Hook
Method isEnabled = AccessibilityManager.class.getDeclaredMethod("isEnabled");
hook(isEnabled).intercept(chain -> {
Object real = chain.proceed();
if (Boolean.FALSE.equals(real)) {
return false; // System ist bereits aus; Null-Overhead-Durchgriff
}
return isCallerWhitelisted() ? real : false;
});
private static boolean isCallerWhitelisted() {
if (HAS_STACK_WALKER) {
try {
return Api34StackWalker.check();
} catch (Throwable ignored) {}
}
return checkLegacy();
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private static final class Api34StackWalker {
private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
static boolean check() {
return Boolean.TRUE.equals(WALKER.walk(frames -> {
boolean foundTarget = false;
var it = frames.iterator();
while (it.hasNext()) {
var f = it.next();
if ("android.view.accessibility.AccessibilityManager".equals(f.getClassName())) {
foundTarget = true;
} else if (foundTarget) {
return isPackageAllowed(f.getClassName());
}
}
return false;
}));
}
}
4. Häufige Upgrade-Fallstricke und Prüfliste
- Muss
assets/xposed_initbeibehalten werden?- Die moderne API 102 liest nur
META-INF/xposed/java_init.list. Wennassets/xposed_initbeibehalten wird, könnte das alte Framework versuchen, es per Reflection zu laden und einen Fehler auslösen. Es wird empfohlen, es zu entfernen oder leer zu lassen.
- Die moderne API 102 liest nur
compileSdkund Android SDK-Plattform:- Es wird empfohlen,
compileSdkundtargetSdkauf Android 33 oder 34 zu aktualisieren, um moderne Schnittstellen und Java 11/17-Sprachfunktionen problemlos nutzen zu können.
- Es wird empfohlen,
- Gradle-Kompatibilität:
- Wenn die Build-Umgebung JDK 17/21 verwendet, muss sichergestellt werden, dass Gradle Wrapper auf
7.4+(empfohlen7.5.1oder8.x) und AGP auf7.3+aktualisiert wird.
- Wenn die Build-Umgebung JDK 17/21 verwendet, muss sichergestellt werden, dass Gradle Wrapper auf
- Modulerkennung validieren:
- Überprüfen Sie mit einem Entpackungstool das Wurzelverzeichnis der finalen APK. Es muss enthalten:
META-INF/xposed/module.propMETA-INF/xposed/java_init.listMETA-INF/xposed/scope.list
- Überprüfen Sie mit einem Entpackungstool das Wurzelverzeichnis der finalen APK. Es muss enthalten: