Guia Completo de Atualização de Módulo Xposed para LibXposed API 102
Este documento resume o processo padrão, paradigmas de código, especificações de metadados e estratégias de otimização de desempenho para atualizar módulos Xposed tradicionais (baseados na API XposedBridge 54-93) para a moderna LibXposed API 102 (compatível com LSPosed 1.9.3+, Vector e outras estruturas modernas).
Qualquer instrução futura para atualizar outros módulos deve referir-se diretamente a esta especificação.
1. Comparação de Mudanças Principais
| Característica / Dimensão | Xposed Tradicional (API 54-93) | LibXposed Moderno (API 102) |
|---|---|---|
| Coordenada de Dependência | de.robv.android.xposed:api:82 (ou 93) | io.github.libxposed:api:102.0.0 |
| Classe/Interface de Entrada | IXposedHookLoadPackage, IXposedHookZygoteInit | Herda io.github.libxposed.api.XposedModule |
| Declaração de Entrada | assets/xposed_init | META-INF/xposed/java_init.list |
| Configuração de Propriedades do Módulo | <meta-data> no AndroidManifest.xml | META-INF/xposed/module.prop |
| Declaração de Escopo | Recurso de array xposedscope | META-INF/xposed/scope.list |
| Modelo de Programação de Hook | Callbacks XC_MethodHook (before / after) | Interceptador encadeado estilo OkHttp Hooker (chain.proceed()) |
| Recarga a Quente (Hot Reload) | Não suportado (reinicialização do aplicativo alvo ou do sistema necessária) | Suportado nativamente (sobrescrever onHotReloading / onHotReloaded) |
| Injeção no Zygote | Suporta injeção global initZygote | Injeção no Zygote proibida, injeta apenas em processos alvo no escopo |
| Suporte a Ofuscação/R8 | Necessário preservar rigorosamente nomes de métodos e classes de hook | Suporta ofuscação R8 nativamente, apenas declarar regras de adaptação |
2. Método Padrão de Migração em Quatro Etapas
Primeira Etapa: Migração de Build e Dependência (build.gradle)
-
Atualizar Dependências: Substitua a dependência no
build.gradledo módulo (ex.:app/build.gradleouhook/build.gradle):dependencies { // Remover: compileOnly 'de.robv.android.xposed:api:82' // Introduzir a API moderna LibXposed 102 compileOnly 'io.github.libxposed:api:102.0.0' compileOnly 'androidx.annotation:annotation:1.5.0' } -
Compatibilidade de Compilação Java:
android { compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } } -
Configurar Regras 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>(); }
Segunda Etapa: Configuração de Arquivos de Metadados Modernos (META-INF/xposed/)
Crie os seguintes arquivos no diretório de origem do módulo src/main/resources/META-INF/xposed/:
1. module.prop (Metadados do Módulo)
minApiVersion=102
targetApiVersion=102
autoHotReload=true
exceptionMode=protective
minApiVersion: Versão mínima suportada da API, defina como102.targetApiVersion: Versão alvo da API, defina como102.autoHotReload: Defina comotruepara acionar automaticamente a recarga a quente quando o módulo for atualizado/instalado.exceptionMode:protective(modo de proteção padrão, exceções de Hook não causam travamento do host) oupassthrough.
2. java_init.list (Declaração da Classe de Entrada)
Declare o nome de classe totalmente qualificado da entrada do módulo em uma única linha:
com.example.myhook.MainHook
3. scope.list (Escopo Recomendado)
Um nome de pacote alvo por linha, declarando os aplicativos nos quais o módulo deve ser injetado por padrão:
com.android.chrome
com.microsoft.emmx
com.example.targetapp
Terceira Etapa: Configuração do Arquivo de Manifesto (AndroidManifest.xml)
Atualize o AndroidManifest.xml do módulo:
<application ...>
<!-- Manter metadados tradicionais para compatibilidade, chave é definir xposedminversion como 102 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Descrição da função do módulo" />
<meta-data
android:name="xposedminversion"
android:value="102" />
<meta-data
android:name="xposedscope"
android:resource="@array/xposed_scope" />
</application>
Quarta Etapa: Refatoração da Lógica de Hook (XposedModule)
1. Modelo Básico e Ciclo de Vida
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();
}
/**
* Acionado quando o ClassLoader do aplicativo host está pronto e o Application está prestes a ser criado
*/
@Override
public void onPackageReady(@NonNull PackageReadyParam param) {
if (mHooked) return;
synchronized (this) {
if (mHooked) return;
mHooked = true;
initHooks(param.getPackageName(), param.getClassLoader());
}
}
/**
* Callback de preparação para recarga a quente (executado no código da versão antiga)
* Retorne true para permitir que a estrutura substitua pela nova versão
*/
@Override
public boolean onHotReloading(@NonNull HotReloadingParam param) {
return true;
}
/**
* Callback de conclusão de recarga a quente (executado no código da nova versão)
*/
@Override
public void onHotReloaded(@NonNull HotReloadedParam param) {
// Desfazer todos os hooks registrados pela versão antiga
param.getOldHookHandles().forEach(XposedInterface.HookHandle::unhook);
synchronized (this) {
mHooked = false;
initHooks("hot-reload", getClass().getClassLoader());
}
}
private void initHooks(String packageName, ClassLoader classLoader) {
// Executar lógica de hook específica
}
}
2. Sintaxe de Hook Central - Comparação
| Operação Alvo | Escrita Xposed Tradicional | Escrita Moderna LibXposed 102 |
|---|---|---|
| Encontrar e Hookar Método | XposedHelpers.findAndHookMethod("NomeClasse", cl, "metodo", argTypes..., callback) | Method m = Clazz.class.getDeclaredMethod("metodo", argTypes...);hook(m).intercept(chain -> { ... }); |
| Executar Método Original | Executado diretamente por padrão; chamar param.getResult() | Object result = chain.proceed(); |
| Modificar Parâmetros e Chamar | param.args[0] = novoVal; | Object result = chain.proceed(new Object[]{ novoVal, ... }); |
| Interceptar e Substituir Valor de Retorno | param.setResult(valorCustomizado); | Retornar valor customizado diretamente (sem chamar chain.proceed()):hook(m).intercept(chain -> valorCustomizado); |
| Obter Parâmetros de Entrada | param.args[i] | chain.getArg(i) ou chain.getArgs() |
| Obter Instância this | param.thisObject | chain.getThisObject() |
| Hookar Construtor | findAndHookConstructor(...) | Constructor<?> ctor = ...;hook(ctor).intercept(chain -> ...); |
| Hookar Bloco Static Initializer | API padrão não suportada tradicionalmente | hookClassInitializer(TargetClass.class).intercept(...) |
3. Cenários de Hook de Alta Frequência e Padrões de Otimização de Desempenho Profundo
Regra de Ouro para Otimização de Chamadas de Alta Frequência (ex.: isEnabled, renderização de View, despacho de toque):
-
Caminho Rápido com Sobrecarga Zero (Sondagem Direta): Se o método é um método de consulta de estado (ex.:
isEnabled(),isLoaded()), primeiro chame a leitura de campo nativo levechain.proceed(). Se o estado real for falso, retorne falso imediatamente, pulando toda a lógica subsequente e verificações de pilha. -
Android 14+ (API 34+) Usar
StackWalkerpara Iteração Preguiçosa: O tradicionalnew Throwable().getStackTrace()causa expansão profunda da pilha de threads (Native Unwind) e centenas de alocações de string, facilmente causando queda de quadros devido ao GC. Usejava.lang.StackWalkerdo Java 9 / Android 14+ para escanear camada por camada sob demanda, com zero alocação de pilha completa. -
Localização de Pilha Dinâmica para Android < 34 (Evitar Endereços Fixos): Execute
new Throwable().getStackTrace()apenas uma vez, itere para baixo para encontrar a classe do sistema onde o método alvo reside (ex.:AccessibilityManager), o quadro de pilha imediatamente seguinte é o verdadeiro chamador. Nunca codifique[3]ou[4], garantindo operação estável entre versões do LSPosed.
Código de Exemplo de Otimização de Desempenho:
// Verificar StackWalker do 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;
}
// Exemplo de Hook de Método de Alta Frequência
Method isEnabled = AccessibilityManager.class.getDeclaredMethod("isEnabled");
hook(isEnabled).intercept(chain -> {
Object real = chain.proceed();
if (Boolean.FALSE.equals(real)) {
return false; // Sistema já está desligado, retorno direto com sobrecarga zero
}
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. Armadilhas Comuns de Atualização e Lista de Verificação
assets/xposed_initainda precisa ser mantido?- A API moderna 102 lê apenas
META-INF/xposed/java_init.list. Seassets/xposed_initfor mantido, estruturas antigas podem tentar usar reflexão tradicional para carregá-lo e gerar erros. Recomenda-se removê-lo diretamente ou mantê-lo vazio.
- A API moderna 102 lê apenas
compileSdke Plataforma Android SDK:- Recomenda-se atualizar
compileSdketargetSdkpara Android 33 ou 34 para usar interfaces modernas e recursos de linguagem Java 11/17 sem problemas.
- Recomenda-se atualizar
- Compatibilidade do Gradle:
- Se o ambiente de build usar JDK 17/21, certifique-se de atualizar o Gradle Wrapper para
7.4+(recomendado7.5.1ou8.x) e o AGP para7.3+.
- Se o ambiente de build usar JDK 17/21, certifique-se de atualizar o Gradle Wrapper para
- Verificar Reconhecimento do Módulo:
- Use uma ferramenta de descompressão para verificar o diretório raiz do APK final. Ele deve conter:
META-INF/xposed/module.propMETA-INF/xposed/java_init.listMETA-INF/xposed/scope.list
- Use uma ferramenta de descompressão para verificar o diretório raiz do APK final. Ele deve conter: