CommunityRédaction et éditiongithub.com

Marukon/skill-upgrade-libxposed-102

Guide complet pour migrer les modules Xposed traditionnels vers l'API moderne LibXposed 102 (LSPosed 1.9.3+, Vector, etc.).

Qu'est-ce que skill-upgrade-libxposed-102 ?

Ce guide technique détaille la procédure complète de migration des modules Xposed utilisant l'ancienne API (XposedBridge 54-93) vers la nouvelle API LibXposed 102, compatible avec les frameworks modernes comme LSPosed 1.9.3+ et Vector. Il couvre les quatre étapes principales : mise à jour des dépendances Gradle (remplacement de `de.robv.android.xposed:api:82` par `io.github.libxposed:api:102.0.0`), configuration des métadonnées modernes (fichiers `module.prop`, `java_init.list`, `scope.list` dans `META-INF/xposed/`), adaptation du `AndroidManifest.xml`, et surtout la refonte complète des hooks. La principale nouveauté est le changement du modèle de programmation : on passe des callbacks `XC_MethodHook` (before/after) à un modèle de chaîne d'interception de type OkHttp avec l'interface `Hooker` et la méthode `chain.proceed()`. Le guide met également l'accent sur l'optimisation des performances pour les méthodes à haute fréquence d'appel (comme `isEnabled()` d'AccessibilityManager) en utilisant `StackWalker` sur Android 14+ et en évitant les allocations coûteuses. Il couvre des sujets avancés comme le hot reload natif, l'interdiction de l'injection Zygote, la gestion de la protection contre les exceptions (`exceptionMode`), et la configuration R8 pour l'obfuscation.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add Marukon/skill-upgrade-libxposed-102

Installed? Explore more Rédaction et édition skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Guide complet de mise à niveau des modules Xposed vers LibXposed API 102

Ce document résume le processus standard, les paradigmes de code, les spécifications des métadonnées et les stratégies d'optimisation des performances pour mettre à niveau les modules Xposed traditionnels (basés sur XposedBridge API 54-93) vers la LibXposed API 102 moderne (compatible avec LSPosed 1.9.3+, Vector et autres frameworks modernes).

Pour toute instruction ultérieure de mise à niveau d'autres modules, référez-vous directement à cette spécification.


I. Comparaison des changements fondamentaux

Caractéristique / DimensionXposed traditionnel (API 54-93)LibXposed moderne (API 102)
Coordonnées de dépendancede.robv.android.xposed:api:82 (ou 93)io.github.libxposed:api:102.0.0
Classe/Interface d'entréeIXposedHookLoadPackage, IXposedHookZygoteInitHériter de io.github.libxposed.api.XposedModule
Déclaration d'entréeassets/xposed_initMETA-INF/xposed/java_init.list
Configuration des propriétés du module<meta-data> dans AndroidManifest.xmlMETA-INF/xposed/module.prop
Déclaration de portéeTableau de ressources xposedscopeMETA-INF/xposed/scope.list
Modèle de programmation HookCallbacks XC_MethodHook (before / after)Intercepteur chaîné de type OkHttp avec Hooker (chain.proceed())
Rechargement à chaud (Hot Reload)Non supporté (nécessite de redémarrer l'application cible, voire le système)Support natif (réécrire onHotReloading / onHotReloaded)
Injection ZygoteSupporte l'injection globale initZygoteInjection Zygote interdite, injection uniquement dans les processus cibles de la portée
Support d'obfuscation/R8Nécessité de préserver strictement les noms de méthodes et de classes HookSupport natif de l'obfuscation R8, déclaration de règles d'adaptation suffisante

II. Méthode standard de migration en quatre étapes

Première étape : Migration des dépendances et de la construction (build.gradle)

  1. Mettre à jour les dépendances : Dans le fichier build.gradle du module (par exemple app/build.gradle ou hook/build.gradle), remplacez les dépendances :

    dependencies {
        // Supprimer : compileOnly 'de.robv.android.xposed:api:82'
        // Introduire la nouvelle API LibXposed 102
        compileOnly 'io.github.libxposed:api:102.0.0'
        compileOnly 'androidx.annotation:annotation:1.5.0'
    }
    
  2. Compatibilité de compilation Java :

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_11
            targetCompatibility JavaVersion.VERSION_11
        }
    }
    
  3. Configurer les règles ProGuard / R8 (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>();
    }
    

Deuxième étape : Configuration des fichiers de métadonnées modernes (META-INF/xposed/)

Créez les fichiers suivants dans le répertoire src/main/resources/META-INF/xposed/ du module source :

1. module.prop (métadonnées du module)

minApiVersion=102
targetApiVersion=102
autoHotReload=true
exceptionMode=protective
  • minApiVersion: Version minimale de l'API prise en charge, définissez 102.
  • targetApiVersion: Version cible de l'API, définissez 102.
  • autoHotReload: Définissez true pour déclencher automatiquement un rechargement à chaud lors de l'installation d'une mise à jour du module.
  • exceptionMode: protective (mode de protection par défaut, une exception dans un Hook n'affecte pas le crash de l'hôte) ou passthrough.

2. java_init.list (déclaration des classes d'entrée)

Déclarez le nom de classe complet du point d'entrée du module sur une seule ligne :

com.example.myhook.MainHook

3. scope.list (portée recommandée)

Un nom de package cible par ligne, déclare les applications dans lesquelles le module doit être injecté par défaut :

com.android.chrome
com.microsoft.emmx
com.example.targetapp

Troisième étape : Configuration du fichier Manifest (AndroidManifest.xml)

Mettez à jour le AndroidManifest.xml du module :

<application ...>
    <!-- Les métadonnées traditionnelles restent compatibles, l'essentiel est de définir xposedminversion sur 102 -->
    <meta-data
        android:name="xposedmodule"
        android:value="true" />
    <meta-data
        android:name="xposeddescription"
        android:value="Description des fonctionnalités du module" />
    <meta-data
        android:name="xposedminversion"
        android:value="102" />
    <meta-data
        android:name="xposedscope"
        android:resource="@array/xposed_scope" />
</application>

Quatrième étape : Refonte de la logique des Hooks (XposedModule)

1. Modèle de base et cycle de vie

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();
    }

    /**
     * Déclenché lorsque le ClassLoader de l'application hôte est prêt et que l'Application est sur le point d'être créée
     */
    @Override
    public void onPackageReady(@NonNull PackageReadyParam param) {
        if (mHooked) return;
        synchronized (this) {
            if (mHooked) return;
            mHooked = true;
            initHooks(param.getPackageName(), param.getClassLoader());
        }
    }

    /**
     * Callback de préparation au rechargement à chaud (s'exécute dans l'ancienne version du code)
     * Retourne true pour permettre au framework de remplacer par la nouvelle version
     */
    @Override
    public boolean onHotReloading(@NonNull HotReloadingParam param) {
        return true;
    }

    /**
     * Callback de fin de rechargement à chaud (s'exécute dans la nouvelle version du code)
     */
    @Override
    public void onHotReloaded(@NonNull HotReloadedParam param) {
        // Désenregistrer tous les Hooks enregistrés par l'ancienne version
        param.getOldHookHandles().forEach(XposedInterface.HookHandle::unhook);
        synchronized (this) {
            mHooked = false;
            initHooks("hot-reload", getClass().getClassLoader());
        }
    }

    private void initHooks(String packageName, ClassLoader classLoader) {
        // Exécution de la logique de Hook spécifique
    }
}

2. Correspondance de la syntaxe de Hook principale

Opération cibleSyntaxe Xposed traditionnelleSyntaxe moderne LibXposed 102
Trouver et Hook une méthodeXposedHelpers.findAndHookMethod("NomClasse", cl, "méthode", typesArg..., callback)Method m = Clazz.class.getDeclaredMethod("méthode", typesArg...);hook(m).intercept(chain -> { ... });
Exécuter la méthode originaleExécution par défaut ; appeler param.getResult()Object result = chain.proceed();
Modifier un argument et appelerparam.args[0] = newVal;Object result = chain.proceed(new Object[]{ newVal, ... });
Intercepter et remplacer la valeur de retourparam.setResult(customVal);Retourner directement une valeur personnalisée (sans appeler chain.proceed()) :hook(m).intercept(chain -> customVal);
Obtenir les paramètres d'entréeparam.args[i]chain.getArg(i) ou chain.getArgs()
Obtenir l'instance thisparam.thisObjectchain.getThisObject()
Hook un constructeurfindAndHookConstructor(...)Constructor<?> ctor = ...;hook(ctor).intercept(chain -> ...);
Hook un bloc statiqueNon supporté par l'API standard traditionnellehookClassInitializer(TargetClass.class).intercept(...)

III. Scénarios de Hook à haute fréquence et modèles d'optimisation des performances approfondis

Règle d'or d'optimisation pour les appels à haute fréquence (comme isEnabled, rendu View, distribution tactile) :

  1. Chemin rapide à zéro surcoût (sondage direct) : Si la méthode est une méthode de requête d'état (comme isEnabled(), isLoaded()), appelez d'abord la lecture de champ natif légère chain.proceed(). Si l'état réel est faux, retournez directement faux, sautez toute la logique ultérieure et la vérification de la pile.

  2. Android 14+ (API 34+) : Utiliser StackWalker pour une traversée paresseuse : new Throwable().getStackTrace() traditionnel provoque un déroulement complet de la pile de threads (Native Unwind) et des centaines d'allocations de chaînes, entraînant facilement des chutes de trames GC. Utilisez java.lang.StackWalker de Java 9 / Android 14+ pour une analyse couche par couche à la demande, zéro allocation de pile complète.

  3. Android < 34 : Localisation dynamique de la pile (pas de codage en dur) : Exécutez new Throwable().getStackTrace() une seule fois, parcourez vers le bas pour trouver la classe système où se trouve la méthode cible (comme AccessibilityManager), le cadre de pile immédiatement suivant est le véritable appelant. Ne codez jamais en dur [3] ou [4], garantissant un fonctionnement stable entre les versions LSPosed.

Code d'exemple d'optimisation des performances :

// Vérifier StackWalker Android 14+
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;
}

// Exemple de Hook de méthode à haute fréquence
Method isEnabled = AccessibilityManager.class.getDeclaredMethod("isEnabled");
hook(isEnabled).intercept(chain -> {
    Object real = chain.proceed();
    if (Boolean.FALSE.equals(real)) {
        return false; // Le système est déjà désactivé, retour direct à zéro surcoût
    }
    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;
        }));
    }
}

IV. Pièges courants de mise à niveau et liste de vérification

  1. assets/xposed_init doit-il être conservé ?
    • L'API 102 moderne lit uniquement META-INF/xposed/java_init.list. Si assets/xposed_init est conservé, les anciens frameworks peuvent tenter de l'utiliser avec une réflexion traditionnelle et générer une erreur. Il est recommandé de le supprimer directement ou de le laisser vide.
  2. compileSdk et plateforme Android SDK :
    • Il est suggéré de mettre à niveau compileSdk et targetSdk vers Android 33 ou 34 pour utiliser facilement les interfaces modernes et les fonctionnalités du langage Java 11/17.
  3. Compatibilité Gradle :
    • Si l'environnement de construction utilise JDK 17/21, assurez-vous que le wrapper Gradle est mis à niveau vers 7.4+ (7.5.1 ou 8.x recommandé), et que AGP est mis à niveau vers 7.3+.
  4. Vérification de la reconnaissance du module :
    • Utilisez un outil de décompression pour vérifier le répertoire racine de l'APK final. Il doit contenir :
      • META-INF/xposed/module.prop
      • META-INF/xposed/java_init.list
      • META-INF/xposed/scope.list

Skills associés