今天来做一下Xposed模块环境的搭建,方便我们去编写一些模块去个性化定制一些能力。
我也是第一次写模块,踩了一堆坑终于了解这个流程是什么了。网上教程都不太详细,有一些困惑的点没说清楚到底怎么搞。于是我打算自己重新写一篇教程,尝试把一些细节方面的东西说清楚。

因为Xposed本质上也是安卓APP,所以我们也是使用Android Studio进行开发(安卓死丢丢)。关于安卓死丢丢的下载和安装就不多赘述了,这也不是这个教程的重点。

新建项目

对于XP模块,我建议先从No Activity开始创建,因为其他类型都带了一堆东西,不是很轻量。同时,一个非常干净的代码环境相比来说问题更少。遵循模块越轻量,越稳定的原则。

然后直接下一步就行。

这一步很简单,取个名字就可以。另外我们使用的是kotlin DSL,这是一个更现代的包管理解决方案,它的特点是提供了模块间解耦,分为项目级配置模块级配置项目设置。本教程也会围绕它来展开。
点击“完成”,这一环节就结束了。

配置AndroidManifest.xml

在这一步,目的是声明自己的app为Xposed模块,好让框架发现它。
在你的application节点下添加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 声明此为 Xposed 模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- 模块描述 -->
<meta-data
android:name="xposeddescription"
android:value="你的模块描述信息" />
<!-- 支持的最低 Xposed 版本 -->
<meta-data
android:name="xposedminversion"
android:value="51" />

一般情况下,application节点包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<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"
android:theme="@style/Theme.SystemDefender"
tools:targetApi="31" >

<!-- 声明此为 Xposed 模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- 模块描述 -->
<meta-data
android:name="xposeddescription"
android:value="你的模块描述信息" />
<!-- 支持的最低 Xposed 版本 -->
<meta-data
android:name="xposedminversion"
android:value="82" />

<meta-data
android:name="xposedscope"
android:resource="@array/scope_list" />

</application>

至于这个xposedscope是什么,后面会解释。
声明完成后,就可以去配置依赖了。

设置项目依赖

在你的模块级build.gradle.kts下引入xposed依赖(dependencies块内):

1
2
compileOnly("de.robv.android.xposed:api:82")
// compileOnly 'de.robv.android.xposed:api:82:sources' // 不要导入源码,这会导致idea无法索引文件,从而让语法提示失效

添加后,如果你选择Sync同步修改,会发现IDE没有任何下载提示。不要慌,这是正常的。因为compileOnly只会包含依赖的引用,并不会把依赖本身下载到本地。你可以理解为只是一个链接或声明。相应的,implementation才会把依赖下载到本地。但是这里一定要用compileOnly,因为相关的依赖在运行时环境Xposed框架下已经提供了,如果再次下载会导致安装包内包含的依赖文件和Xposed运行时自带的依赖整个冲突掉。

如果你这时候编译,就会发现编译的时候报错了。那是因为标准Maven仓库里面并没有这个依赖项,我们还需要去添加xposed的专属仓库才可以。
项目设置settings.gradle.kts中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
maven { url = uri("https://api.xposed.info/") }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://api.xposed.info/") }
}
}

rootProject.name = "SystemDefender"
include(":app")

需要同时在pluginManagement/repositoriesdependencyResolutionManagement/repositories节点下添加以下语句:

1
maven { url = uri("https://api.xposed.info/") }

这一步就算完成了。
这时候终于可以通过编译了,但是安装完成以后你就会发现框架还是不认。实际上是只完成了声明“这是一个模块”还有开发依赖的声明,距离它真正读取到还需要添加入口点与入口点声明。

添加hook入口点

创建一个类,它实现了一个最基本的写法:
MainHook.kt(当然你要用Java写也可以,名称可以随意,一般为MainHook)

1
2
3
4
5
6
7
8
9
10
11
12
package xposed.qg46.systemdefender

import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.callbacks.XC_LoadPackage

class MainHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == "android") {
// 在这里添加系统框架的Hook代码
}
}
}

这是一个hook系统框架的代码框架,你可以根据实际需求选择修改成你要对应hook的其他app包名。

下一步就是去声明这个入口类:
你需要在你的项目名称/app/src/main下创建assets文件夹(右键创建的时候需要注意,需要作为资源(Resource)目录创建,而且不能是其他名字),并在里面新建xposed_init文件(注意:不包含任何扩展名,文件名也必须保持一致)
文件内容为你的对应入口类的位置,我这里是:

1
xposed.qg46.systemdefender.MainHook

(如果你起的类名不是MainHook,这里需要同时更改)
当然,你也可能根本找不到main目录在哪。在IDE顶部把视图切换为Project:

这样,就能看到完整目录了。

创建完记得检查一下,assets文件夹AndroidManifest.xml目录同级。
完成这一步以后可以切换回Android视图,写代码更方便。

最终完善

做完上面步骤以后,你就可以编译了。这时候应该就能看到Xp框架识别到你的模块了。
但是你应该早就发现其他模块在启用时候还会显示“推荐应用”,有了这个更方便。
其实这个实现起来也不难,只需要以下步骤:
res/values/下新建arrays.xml文件,内容为

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="scope_list" >
<!-- 这里填写模块的作用域应用的包名,可以填多个item。 -->
<item>android</item>
</string-array>
</resources>

当然,这里文件名string-array的name字段的值都是可以自定义的。只需要声明的时候对应起来就可以。
如果你要hook系统框架,那么这里添加item为android。如果需要其他应用,则是对应包名。
创建完成后,别忘了在AndroidManifest.xml添加对应的声明:

1
2
3
<meta-data
android:name="xposedscope"
android:resource="@array/scope_list" />

这也就是xposedscope的作用了。
完成以后,编译运行,你就可以在xp框架中看到你的这个空白模块了。可以愉快的写代码了。
以下为效果图:

一些重要的事

提醒一下,请在Android Studio上禁用部署优化,或者使用gradlew installDebug命令进行安装,否则无法更新模块。