前言
记一次将 xposed 模块迁移到新版 API 。
最近在想着将 xposed 模块迁移到新版的 API 。LSP 团队在 wiki 上也写上了关于 API 变更的帖子。不过迁移过程还是遇到了一些问题,故记录一下。
正文
依赖
在旧版 API 下,模块需要安装 xposed 的依赖,并将它设置为 compileOnly 。
libs.versions.toml 文件如下:
1 2 3 4 5
| [versions] xposed = "82"
[libraries] xposed = { group = "de.robv.android.xposed", name = "api", version.ref = "xposed" }
|
build.gradle.kts 文件如下:
1 2 3
| dependencies { compileOnly(libs.xposed) }
|
而在新版 API 下,我们需要将这个换成新版的 API 包。
libs.versions.toml 文件如下:
1 2 3 4 5
| [versions] libxposed = "102.0.0"
[libraries] libxposedapi = { group = "io.github.libxposed", name = "api", version.ref = "libxposed" }
|
同样这个包也是 compileOnly 的, build.gradle.kts 文件如下:
1 2 3
| dependencies { compileOnly(libs.libxposedapi) }
|
入口配置
接着去除 src/main/assets/xposed_init 文件,该文件在旧版上的作用为标记入口类,如下:
1
| com.par9uet.fkmojiread.MainHook
|
而在新版上,内容不变,但是要将文件移动到 src/main/resources/META-INF/xposed 下并重命名为 java_init.list 。
作用域
在旧版 API 上,我们是在 AndroidManifest.xml 内在 application 标签内定义元数据来给模块绑定作用域:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> <meta-data android:name="xposedscope" android:resource="@array/xposedscope"/> </application> </manifest>
|
这里的 @array/xposedscope 对应的文件位于 src/main/res/values/array.xml ,内容如下:
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="xposedscope" > <item>com.mojidict.read</item> </string-array> </resources>
|
而在新版 API 下,不是在此处编写作用域了,我们需要在和入口配置同级的目录下,也就是 src/main/resources/META-INF/xposed ,新加一个 scope.list 文件来定义:
注意这里每行对应一个应用。
API 版本标识
在旧版 API 下,版本标识和作用域一样,在 AndroidManifest.xml 内在 application 标签内定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> <meta-data android:name="xposedminversion" android:value="93"/> </application> </manifest>
|
而在新版 API 下,不是在此处编写作用域了,我们需要在和入口配置同级的目录下,也就是 src/main/resources/META-INF/xposed ,新加一个 module.prop 文件来定义:
1 2
| minApiVersion=101 targetApiVersion=102
|
其中 minApiVersion 表明模块所需的最低版本,新版 API 的第一个版本就是 101 ,所以一般这里最低就是 101 了, targetApiVersion 则表明该模块引入的版本,目前最新的是 102 。所以这个根据实际情况编写。
这个文件是一定要存在的,不然无法被 Lsposed 识别成模块,当时迁移的时候没写这个文件,百思不得其解为啥没识别成模块,最后还是上 github 看了一些基于新版 API 的模块,才知道缺了这个文件…
模块描述
在旧版 API 下,模块描述和作用域一样,在 AndroidManifest.xml 内在 application 标签内定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> <meta-data android:name="xposeddescription" android:value="FKMOJi阅读" /> </application> </manifest>
|
而在新版 API 下,需要改为在 application 标签上使用 android:description 来定义:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:description="@string/app_description" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> </application> </manifest>
|
在 src/main/res/values/strings.xml 添加一行 app_description :
1 2 3 4
| <resources> <string name="app_name">fkmojiread</string> <string name="app_description">解锁永久会员部分功能</string> </resources>
|
hook
在旧版 API 下,入口类需要实现 IXposedHookLoadPackage 接口,然后实现 handleLoadPackage 方法,在里面写 hook 逻辑。
1 2 3 4 5 6 7 8 9 10
| public class MainHook implements IXposedHookLoadPackage {
@Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam param) { if (!param.packageName.equals("{包名}")) { return; } } }
|
新版 API 下,需要继承 XposedModule ,然后实现 onPackageLoaded 来注入 hook 逻辑:
1 2 3 4 5 6 7 8 9 10
| public class MainHook extends XposedModule {
@Override public void onPackageLoaded(PackageLoadedParam param) { if (!param.getPackageName().equals("{包名}")) { return; } } }
|
关于 XposedHelpers
旧版 API 提供了 XposedHelpers 工具类,能少写很多样板代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class MainHook implements IXposedHookLoadPackage {
void hook(XC_LoadPackage.LoadPackageParam param) { XposedHelpers.findAndHookMethod( "V5.a", param.classLoader, "a", String.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) {}
@Override protected void afterHookedMethod(MethodHookParam param) {} } ); } }
|
虽然在 wiki 里说有提供新版的 helper :
![]()
但是很遗憾,目前这个包并没有提供 maven 包供使用,而且源码似乎还有一些问题,所以能做的就是把这里面的源码拷贝到自己的模块里面构建。
这里有几个要注意的问题,首先是 org.apache.commons:commons-lang3 提供的 MemberUtilsX 并没有暴露,但源码里面导入了这个,有点没搞懂,是我配置的问题么,我的解决方法就是把这个类直接复制出来。
在把 MemberUtilsX 复制出来后,会提示缺少 AccessibleObjects ,这个类也是没有暴露出来的,同样也是复制出来即可,实现逻辑很简单。
![]()
在把 package 定义都修改之后,基本上就能打包了,但是如果使用的话,会发现空指针错误,来源是 XposedBridge 内的静态 api 属性为空:
![]()
这里我的解决方法就是手动调用它的 init 方法,调用时机的话继承 XposedModule 的 onModuleLoaded 上,将自身传入 init :
1 2 3 4 5 6 7 8
| public class MainHook extends XposedModule {
@Override public void onModuleLoaded(@NonNull ModuleLoadedParam param) { super.onModuleLoaded(param); XposedBridge.init(this); } }
|
之所以能这么写,原因是 XposedModule 继承了 XposedInterfaceWrapper ,而 XposedInterfaceWrapper 实现了 XposedInterface ,所以模块自身其实也就是一个 XposedInterface 。这样子过后,基本上就可以无痛将以前的逻辑迁移过来了。
后记
省流步骤总结:
- 安装新版依赖。
- 调整
xposed_init 位置,并重命名为 java_init.list 。
- 去除
AndroidManifest.xml 里的元数据。
- 新增
scope.list 和 module.prop 文件。
- 实现
IXposedHookLoadPackage 改为继承 XposedModule ,并实现对应方法。
- 迁移 helper 库(如果需要的话)。
如果还有疑问,也也可以查看我的仓库:Dedicatus546/fkmojiread 。目前也是成功迁移到了新版 API 。