diff --git a/README.md b/README.md
index b0c15c66..2ee8a73f 100644
--- a/README.md
+++ b/README.md
@@ -11,14 +11,6 @@
⛱️ An efficient Kotlin version of the Xposed Hook API.
-## Plan is in progress
-
-> `IXposedHookInitPackageResources` and `IXposedHookZygoteInit` are being adapted recently,let's stay tuned
-
-**计划进行中**
-
-> 近期正在适配 `IXposedHookInitPackageResources`、`IXposedHookZygoteInit` 敬请期待
-
## What's this
- This is an efficient Xposed Hook API rebuilt in Kotlin
@@ -31,6 +23,18 @@
- 名称取自 [《ももくり》女主 栗原 雪(Yuki)](https://www.bilibili.com/bangumi/play/ss5016)
- 前身为 [开发学习项目](https://github.com/fankes/TMore) 中使用的 Innocent Xposed API,现在重新命名并开源
+## Supports
+
+- Standard Hook
+- Zygote Hook
+- Resources Hook
+
+**支持的功能**
+
+- 标准 Hook
+- Zygote Hook
+- 资源钩子(Resources Hook)
+
## Get Started
- [Click here](https://fankes.github.io/YukiHookAPI) go to the documentation page for more detailed tutorials and content.
diff --git a/docs/about/future.md b/docs/about/future.md
index a3b8d3b0..7b511902 100644
--- a/docs/about/future.md
+++ b/docs/about/future.md
@@ -26,15 +26,7 @@
目前 API 只支持通过自动处理程序绑定到 `xposed_init`,若您不喜欢自动处理程序,一定要自己实现模块装载入口,未来会按照需求人数推出仅有 API 功能的 Lite 版本。
-### 支持资源 Hook 和注入系统框架
-
-**计划状态:近期**
-
-目前的 API 仅支持 APP 内的功能 Hook,并不支持 `Resource` 的替换以及 Hook 系统框架。
-
-API 还未实现对 `handleInitPackageResources` 和 `initZygote` 的调用。
-
-在未来会根据使用和需求人数加上这个功能,如有需求你也可以向我们提交 Pull Request 来贡献你的代码。
+API 已经提供了 Xposed 原生 API 监听接口,你可以 [在这里](config/xposed-using?id=原生-xposed-api-事件) 找到或查看 Demo 的实现方法。
### 支持更多 Hook Framework
diff --git a/docs/api/document.md b/docs/api/document.md
index 481232f2..0d277858 100644
--- a/docs/api/document.md
+++ b/docs/api/document.md
@@ -22,6 +22,14 @@
[filename](public/ModuleApplication.md ':include')
+[filename](public/YukiModuleResources.md ':include')
+
+[filename](public/YukiResources.md ':include')
+
+[filename](public/YukiResForwarder.md ':include')
+
+[filename](public/YukiXposedEvent.md ':include')
+
[filename](public/ComponentTypeFactory.md ':include')
[filename](public/GraphicsTypeFactory.md ':include')
@@ -38,7 +46,9 @@
[filename](public/YukiBaseHooker.md ':include')
-[filename](public/YukiHookCreater.md ':include')
+[filename](public/YukiMemberHookCreater.md ':include')
+
+[filename](public/YukiResourcesHookCreater.md ':include')
[filename](public/MethodFinder.md ':include')
@@ -54,4 +64,6 @@
[filename](public/VariousClass.md ':include')
-[filename](public/CurrentClass.md ':include')
\ No newline at end of file
+[filename](public/CurrentClass.md ':include')
+
+[filename](public/HookResources.md ':include')
\ No newline at end of file
diff --git a/docs/api/public/ConstructorFinder.md b/docs/api/public/ConstructorFinder.md
index c1135f40..a2a6837f 100644
--- a/docs/api/public/ConstructorFinder.md
+++ b/docs/api/public/ConstructorFinder.md
@@ -1,7 +1,7 @@
## ConstructorFinder [class]
```kotlin
-class ConstructorFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder()
+class ConstructorFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder()
```
**变更记录**
@@ -112,6 +112,22 @@ fun paramCount(num: Int): IndexTypeCondition
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
+### superClass [method]
+
+```kotlin
+fun superClass(isOnlySuperClass: Boolean)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置在 `classSet` 的所有父类中查找当前 `Constructor`。
+
+!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。
+
### RemedyPlan [class]
```kotlin
diff --git a/docs/api/public/CurrentClass.md b/docs/api/public/CurrentClass.md
index d9b4b398..68ded49c 100644
--- a/docs/api/public/CurrentClass.md
+++ b/docs/api/public/CurrentClass.md
@@ -1,7 +1,7 @@
## CurrentClass [class]
```kotlin
-class CurrentClass(private val instance: Class<*>, private val self: Any)
+class CurrentClass(internal val instance: Class<*>, internal val self: Any)
```
**变更记录**
@@ -12,10 +12,24 @@ class CurrentClass(private val instance: Class<*>, private val self: Any)
> 当前实例的类操作对象。
+### superClass [method]
+
+```kotlin
+fun superClass(): SuperClass
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 调用父类实例。
+
### field [method]
```kotlin
-fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance
+inline fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance
```
**变更记录**
@@ -29,7 +43,7 @@ fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance
### method [method]
```kotlin
-fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance
+inline fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance
```
**变更记录**
@@ -38,4 +52,46 @@ fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance
**功能描述**
-> 调用当前实例中的方法。
\ No newline at end of file
+> 调用当前实例中的方法。
+
+### SuperClass [class]
+
+```kotlin
+inner class SuperClass
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 当前类的父类实例的类操作对象。
+
+#### field [method]
+
+```kotlin
+inline fun field(initiate: FieldFinder.() -> Unit): FieldFinder.Result.Instance
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 调用父类实例中的变量。
+
+#### method [method]
+
+```kotlin
+inline fun method(initiate: MethodFinder.() -> Unit): MethodFinder.Result.Instance
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 调用父类实例中的方法。
\ No newline at end of file
diff --git a/docs/api/public/FieldFinder.md b/docs/api/public/FieldFinder.md
index 3e276e6a..ce105c9f 100644
--- a/docs/api/public/FieldFinder.md
+++ b/docs/api/public/FieldFinder.md
@@ -1,7 +1,7 @@
## FieldFinder [class]
```kotlin
-class FieldFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>?) : BaseFinder()
+class FieldFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>?) : BaseFinder()
```
**变更记录**
@@ -132,6 +132,22 @@ fun type(value: Any): IndexTypeCondition
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
+### superClass [method]
+
+```kotlin
+fun superClass(isOnlySuperClass: Boolean)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置在 `classSet` 的所有父类中查找当前 `Field`。
+
+!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。
+
### Result [class]
```kotlin
diff --git a/docs/api/public/HookParam.md b/docs/api/public/HookParam.md
index d11c8785..4d0e842b 100644
--- a/docs/api/public/HookParam.md
+++ b/docs/api/public/HookParam.md
@@ -1,7 +1,7 @@
## HookParam [class]
```kotlin
-class HookParam(private val createrInstance: YukiHookCreater, private val wrapper: HookParamWrapper)
+class HookParam(private val createrInstance: YukiMemberHookCreater, private var wrapper: HookParamWrapper?)
```
**变更记录**
diff --git a/docs/api/public/HookResources.md b/docs/api/public/HookResources.md
new file mode 100644
index 00000000..2fcfb700
--- /dev/null
+++ b/docs/api/public/HookResources.md
@@ -0,0 +1,13 @@
+## HookResources [class]
+
+```kotlin
+class HookResources(var instance: YukiResources?)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 创建一个当前 Hook 的 `YukiResources` 接管类。
\ No newline at end of file
diff --git a/docs/api/public/IYukiHookXposedInit.md b/docs/api/public/IYukiHookXposedInit.md
index 86d820ff..099a054d 100644
--- a/docs/api/public/IYukiHookXposedInit.md
+++ b/docs/api/public/IYukiHookXposedInit.md
@@ -48,4 +48,42 @@ fun onHook()
**功能描述**
-> Xposed API 的模块装载调用入口方法。
\ No newline at end of file
+> Xposed API 的模块装载调用入口方法。
+
+### onXposedEvent [method]
+
+```kotlin
+fun onXposedEvent()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 监听 Xposed 原生装载事件。
+
+若你的 Hook 事件中存在需要兼容的原生 Xposed 功能,可在这里实现。
+
+请在这里使用 `YukiXposedEvent` 创建回调事件监听。
+
+可监听的事件如下:
+
+`YukiXposedEvent.onInitZygote`
+
+`YukiXposedEvent.onHandleLoadPackage`
+
+`YukiXposedEvent.onHandleInitPackageResources`
+
+!> 此接口仅供监听和实现原生 Xposed API 的功能,请不要在这里操作 `YukiHookAPI`。
+
+## ~~YukiHookXposedInitProxy [interface]~~
+
+**变更记录**
+
+`v1.0` `添加`
+
+`v1.0.80` `作废`
+
+请转移到 `IYukiHookXposedInit`
\ No newline at end of file
diff --git a/docs/api/public/MethodFinder.md b/docs/api/public/MethodFinder.md
index 5dee8715..38f254f8 100644
--- a/docs/api/public/MethodFinder.md
+++ b/docs/api/public/MethodFinder.md
@@ -1,7 +1,7 @@
## MethodFinder [class]
```kotlin
-class MethodFinder(override val hookInstance: YukiHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder()
+class MethodFinder(override val hookInstance: YukiMemberHookCreater.MemberHookCreater?, override val classSet: Class<*>) : BaseFinder()
```
**变更记录**
@@ -196,6 +196,22 @@ fun returnType(value: Any): IndexTypeCondition
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
+### superClass [method]
+
+```kotlin
+fun superClass(isOnlySuperClass: Boolean)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置在 `classSet` 的所有父类中查找当前 `Method`。
+
+!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。
+
### RemedyPlan [class]
```kotlin
diff --git a/docs/api/public/PackageParam.md b/docs/api/public/PackageParam.md
index 501d0b83..6d3fad33 100644
--- a/docs/api/public/PackageParam.md
+++ b/docs/api/public/PackageParam.md
@@ -1,7 +1,7 @@
## PackageParam [class]
```kotlin
-open class PackageParam(private var wrapper: PackageParamWrapper?)
+open class PackageParam(internal var wrapper: PackageParamWrapper?)
```
**变更记录**
@@ -54,6 +54,24 @@ val appContext: Application
> 获取当前 Hook APP 的 `Application`。
+!> 首次装载可能是空的,请延迟一段时间再获取。
+
+### appResources [field]
+
+```kotlin
+val appResources:Resources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获取当前 Hook APP 的 Resources。
+
+!> 你只能在 `HookResources.hook` 方法体内或 `appContext` 装载完毕时进行调用。
+
### processName [field]
```kotlin
@@ -112,6 +130,38 @@ val mainProcessName: String
其对应的就是 `packageName`。
+### moduleAppFilePath [field]
+
+```kotlin
+val moduleAppFilePath: String
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获取当前 Xposed 模块自身 APK 文件路径。
+
+!> 作为 Hook API 装载时无法使用,会获取到空字符串。
+
+### moduleAppResources [field]
+
+```kotlin
+val moduleAppResources: YukiModuleResources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获取当前 Xposed 模块自身 `Resources`。
+
+!> 作为 Hook API 或不支持的 Hook Framework 装载时无法使用,会抛出异常。
+
### prefs [field]
```kotlin
@@ -126,6 +176,8 @@ val prefs: YukiHookModulePrefs
> 获得当前使用的存取数据对象缓存实例。
+!> 作为 Hook API 装载时无法使用,会抛出异常。
+
### prefs [method]
```kotlin
@@ -146,6 +198,24 @@ fun prefs(name: String): YukiHookModulePrefs
你可以通过 `name` 来自定义 Sp 存储的名称。
+!> 作为 Hook API 装载时无法使用,会抛出异常。
+
+### resources [method]
+
+```kotlin
+fun resources(): HookResources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获得当前 Hook APP 的 `YukiResources` 对象。
+
+请调用 `HookResources.hook` 方法开始 Hook。
+
### loadApp [method]
```kotlin
@@ -170,6 +240,28 @@ fun loadApp(name: String, hooker: YukiBaseHooker)
`name` 为 APP 的包名,后方的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
+装载并 Hook 指定、全部包名的 APP,若要 Hook 系统框架,请使用 `loadZygote`。
+
+### loadZygote [method]
+
+```kotlin
+inline fun loadZygote(initiate: PackageParam.() -> Unit)
+```
+
+```kotlin
+fun loadZygote(hooker: YukiBaseHooker)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 装载并 Hook 系统框架。
+
+方法中的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
+
### withProcess [method]
```kotlin
@@ -331,19 +423,19 @@ val variousClass = VariousClass("com.example.demo.DemoClass1", "com.example.demo
### hook [method]
```kotlin
-inline fun String.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result
+inline fun String.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result
```
```kotlin
-inline fun Class<*>.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result
+inline fun Class<*>.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result
```
```kotlin
-inline fun VariousClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result
+inline fun VariousClass.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result
```
```kotlin
-inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreater.() -> Unit): YukiHookCreater.Result
+inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiMemberHookCreater.() -> Unit): YukiMemberHookCreater.Result
```
**变更记录**
@@ -360,7 +452,7 @@ inline fun HookClass.hook(isUseAppClassLoader: Boolean, initiate: YukiHookCreate
`v1.0.3` `修改`
-新增 `YukiHookCreater.Result` 返回值
+新增 `YukiMemberHookCreater.Result` 返回值
`v1.0.70` `修改`
@@ -443,4 +535,34 @@ findClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").hook {
YourClass.hook(isUseAppClassLoader = false) {
// Your code here.
}
-```
\ No newline at end of file
+```
+
+### hook [method]
+
+```kotlin
+inline fun HookResources.hook(initiate: YukiResourcesHookCreater.() -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增
+
+**功能描述**
+
+> Hook APP 的 Resources。
+
+**功能示例**
+
+Resources Hook 为固定用法,获取 `resources` 对象,然后调用 `hook` 方法开始 Hook。
+
+> 示例如下
+
+```kotlin
+resources().hook {
+ // Your code here.
+}
+```
+
+!> 这是固定用法,为了防止发生问题,你不可手动实现任何 `HookResources` 实例执行 `hook` 调用。
+
+将 Resources 的 Hook 设置为这样是为了与 `findClass(...).hook` 做到统一,使得调用起来逻辑不会混乱。
\ No newline at end of file
diff --git a/docs/api/public/ReflectionFactory.md b/docs/api/public/ReflectionFactory.md
index 38abe3c4..62e1ffcb 100644
--- a/docs/api/public/ReflectionFactory.md
+++ b/docs/api/public/ReflectionFactory.md
@@ -8,6 +8,34 @@
> 这是自定义 `Member` 和 `Class` 相关功能的查找匹配以及 `invoke` 的封装类。
+### hookClass [field]
+
+```kotlin
+val Class<*>.hookClass: HookClass
+```
+
+**变更记录**
+
+`v1.0` `添加`
+
+**功能描述**
+
+> 将 `Class` 转换为 `HookClass`。
+
+### normalClass [field]
+
+```kotlin
+val HookClass.normalClass: Class<*>?
+```
+
+**变更记录**
+
+`v1.0` `添加`
+
+**功能描述**
+
+> 将 `HookClass` 转换为 `Class`。
+
### hasClass [field]
```kotlin
@@ -36,33 +64,19 @@ if("com.example.demo.DemoClass".hasClass) {
}
```
-### hookClass [field]
+### hasExtends [field]
```kotlin
-val Class<*>.hookClass: HookClass
+val Class<*>.hasExtends: Boolean
```
**变更记录**
-`v1.0` `添加`
+`v1.0.80` `新增`
**功能描述**
-> 将 `Class` 转换为 `HookClass`。
-
-### normalClass [field]
-
-```kotlin
-val HookClass.normalClass: Class<*>?
-```
-
-**变更记录**
-
-`v1.0` `添加`
-
-**功能描述**
-
-> 将 `HookClass` 转换为 `Class`。
+> 当前 `Class` 是否有继承关系,父类是 `Any` 将被认为没有继承关系。
### classOf [method]
diff --git a/docs/api/public/YukiHookFactory.md b/docs/api/public/YukiHookFactory.md
index d60426b9..eeabe2c0 100644
--- a/docs/api/public/YukiHookFactory.md
+++ b/docs/api/public/YukiHookFactory.md
@@ -50,7 +50,21 @@ fun IYukiHookXposedInit.encase(vararg hooker: YukiBaseHooker)
**功能描述**
-> 在 `IYukiHookXposedInit` 中装载 `YukiHookAPI`。
+> 在 `IYukiHookXposedInit` 中调用 `YukiHookAPI`。
+
+### resources [method]
+
+```kotlin
+fun IYukiHookXposedInit.resources(initiate: ResourcesParam.() -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 在 `IYukiHookXposedInit` 中调用 `YukiHookAPI.resources`。
### modulePrefs [field]
@@ -94,6 +108,20 @@ val Context.processName: String
> 获取当前进程名称。
+### isSupportResourcesHook [field]
+
+```kotlin
+val Any?.isSupportResourcesHook: Boolean
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)。
+
### isModuleActive [field]
```kotlin
diff --git a/docs/api/public/YukiHookCreater.md b/docs/api/public/YukiMemberHookCreater.md
similarity index 89%
rename from docs/api/public/YukiHookCreater.md
rename to docs/api/public/YukiMemberHookCreater.md
index f790d06f..3ac812b5 100644
--- a/docs/api/public/YukiHookCreater.md
+++ b/docs/api/public/YukiMemberHookCreater.md
@@ -1,7 +1,7 @@
-## YukiHookCreater [class]
+## YukiMemberHookCreater [class]
```kotlin
-class YukiHookCreater(private val packageParam: PackageParam, internal val hookClass: HookClass)
+class YukiMemberHookCreater(private val packageParam: PackageParam, internal val hookClass: HookClass)
```
**变更记录**
@@ -14,7 +14,49 @@ class YukiHookCreater(private val packageParam: PackageParam, internal val hookC
**功能描述**
-> `YukiHookAPI` 核心 Hook 实现类。
+> `YukiHookAPI` 的 `Member` 核心 Hook 实现类。
+
+### PRIORITY_DEFAULT [field]
+
+```kotlin
+val PRIORITY_DEFAULT: Int
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 默认 Hook 回调优先级。
+
+### PRIORITY_LOWEST [field]
+
+```kotlin
+val PRIORITY_LOWEST: Int
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 延迟回调 Hook 方法结果。
+
+### PRIORITY_HIGHEST [field]
+
+```kotlin
+val PRIORITY_HIGHEST: Int
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 更快回调 Hook 方法结果。
### instanceClass [field]
@@ -39,7 +81,7 @@ val instanceClass: Class<*>
### injectMember [method]
```kotlin
-inline fun injectMember(tag: String, initiate: MemberHookCreater.() -> Unit): MemberHookCreater.Result
+inline fun injectMember(priority: Int, tag: String, initiate: MemberHookCreater.() -> Unit): MemberHookCreater.Result
```
**变更记录**
@@ -50,6 +92,8 @@ inline fun injectMember(tag: String, initiate: MemberHookCreater.() -> Unit): Me
将方法体进行 inline
+增加 `priority` Hook 优先级
+
**功能描述**
> 注入要 Hook 的方法、构造类。
@@ -76,16 +120,30 @@ injectMember(tag = "KuriharaYuki") {
}
```
+你还可以自定义 `priority`,以控制当前 Hook 对象并列执行的优先级速度。
+
+> 示例如下
+
+```kotlin
+injectMember(priority = PRIORITY_HIGHEST) {
+ // Your code here.
+}
+```
+
### MemberHookCreater [class]
```kotlin
-inner class MemberHookCreater(var tag: String)
+inner class MemberHookCreater(private val priority: Int, internal val tag: String)
```
**变更记录**
`v1.0` `添加`
+`v1.0.80` `修改`
+
+增加 `priority` Hook 优先级
+
**功能描述**
> Hook 核心功能实现类,查找和处理需要 Hook 的方法、构造类。
diff --git a/docs/api/public/YukiModuleResources.md b/docs/api/public/YukiModuleResources.md
new file mode 100644
index 00000000..f9cbbf28
--- /dev/null
+++ b/docs/api/public/YukiModuleResources.md
@@ -0,0 +1,29 @@
+## YukiModuleResources [class]
+
+```kotlin
+class YukiModuleResources(private val baseInstance: XModuleResources) : Resources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 对接 `XModuleResources` 的中间层实例。
+
+### fwd [method]
+
+```kotlin
+fun fwd(resId: Int): YukiResForwarder
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 对接 `XModuleResources.fwd` 方法。
+
+创建 `YukiResForwarder` 与 `XResForwarder` 实例。
\ No newline at end of file
diff --git a/docs/api/public/YukiResForwarder.md b/docs/api/public/YukiResForwarder.md
new file mode 100644
index 00000000..01e1490b
--- /dev/null
+++ b/docs/api/public/YukiResForwarder.md
@@ -0,0 +1,55 @@
+## YukiResForwarder [class]
+
+```kotlin
+class YukiResForwarder(private val baseInstance: XResForwarder)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 对接 `XResForwarder` 的中间层实例。
+
+### instance [field]
+
+```kotlin
+val instance: XResForwarder
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获得 `XResForwarder` 实例。
+
+### id [field]
+
+```kotlin
+val id: Int
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获得当前 APP 的 Resources Id。
+
+### resources [field]
+
+```kotlin
+val resources: Resources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获得当前 APP 的 Resources。
\ No newline at end of file
diff --git a/docs/api/public/YukiResources.md b/docs/api/public/YukiResources.md
new file mode 100644
index 00000000..5cb0ba4f
--- /dev/null
+++ b/docs/api/public/YukiResources.md
@@ -0,0 +1,77 @@
+## YukiResources [class]
+
+```kotlin
+class YukiResources(private val baseInstance: XResources) : Resources
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 对接 `XResources` 的中间层实例。
+
+### LayoutInflatedParam [class]
+
+```kotlin
+class LayoutInflatedParam(internal val baseParam: XC_LayoutInflated.LayoutInflatedParam)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 装载 Hook APP 的目标布局 Resources 实现类。
+
+#### variantName [field]
+
+```kotlin
+val variantName: String
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获取当前被 Hook 的布局装载目录名称。
+
+例如:`layout`、`layout-land`、`layout-sw600dp`。
+
+#### currentView [field]
+
+```kotlin
+val currentView: View
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 获取当前被 Hook 的布局实例。
+
+#### findViewByIdentifier [method]
+
+```kotlin
+inline fun View.findViewByIdentifier(name: String): T?
+```
+
+```kotlin
+inline fun findViewByIdentifier(name: String): T?
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 使用 Identifier 查找 Hook APP 指定 Id 的 `View`。
+
+扩展方法可以使用 Identifier 查找 Hook APP 当前装载布局中指定 Id 的 `View`。
\ No newline at end of file
diff --git a/docs/api/public/YukiResourcesHookCreater.md b/docs/api/public/YukiResourcesHookCreater.md
new file mode 100644
index 00000000..7e719d16
--- /dev/null
+++ b/docs/api/public/YukiResourcesHookCreater.md
@@ -0,0 +1,569 @@
+## YukiResourcesHookCreater [class]
+
+```kotlin
+class YukiResourcesHookCreater(private val packageParam: PackageParam, internal val hookResources: HookResources)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> `YukiHookAPI` 的 `Resources` 核心 Hook 实现类。
+
+### injectResource [method]
+
+```kotlin
+inline fun injectResource(tag: String, initiate: ResourceHookCreater.() -> Unit): ResourceHookCreater.Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 注入要 Hook 的 Resources。
+
+**功能示例**
+
+你可以注入任意 Resources,使用 `injectResource` 即可创建一个 `Hook` 对象。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ // Your code here.
+}
+```
+
+你还可以自定义 `tag`,方便你在调试的时候能够区分你的 `Hook` 对象。
+
+> 示例如下
+
+```kotlin
+injectResource(tag = "KuriharaYuki") {
+ // Your code here.
+}
+```
+
+### ResourcesHookCreater [class]
+
+```kotlin
+inner class ResourcesHookCreater(private val tag: String)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> Hook 核心功能实现类。
+
+查找和处理需要 Hook 的 Resources。
+
+#### resourceId [field]
+
+```kotlin
+var resourceId: Int
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 直接设置需要替换的 Resources Id。
+
+!> 不建议使用此方法设置目标需要 Hook 的 Resources Id,你可以使用 `conditions` 方法。
+
+**功能示例**
+
+你可以直接设置并指定目标 Hook APP 的 Resources Id。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ resourceId = 0x7f060001.toInt()
+ replaceTo(...)
+}
+```
+
+#### conditions [method]
+
+```kotlin
+inline fun conditions(initiate: ConditionFinder.() -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 查找条件。
+
+若你设置了 `resourceId` 则此方法将不会被使用。
+
+**功能示例**
+
+你可参考 [ConditionFinder](#conditionfinder-class) 查看详细用法。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "test_string"
+ string()
+ }
+ replaceTo(...)
+}
+```
+
+#### replaceTo [method]
+
+```kotlin
+fun replaceTo(any: Any)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 替换指定 Resources 为指定的值。
+
+**功能示例**
+
+你可以替换找到的 Resources 为你想要的值,可以是 `String`、`Drawable` 等。
+
+比如我们要替换一个找到的字符串 Resources。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "test_string"
+ string()
+ }
+ replaceTo("replace string")
+}
+```
+
+或是替换为一个 `Drawable`,你无需对目标 Resources 实现 `fwd` 方法或 `DrawableLoader`。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "test_drawable"
+ drawable()
+ }
+ replaceTo(ColorDrawable(Color.RED))
+}
+```
+
+#### replaceToTrue [method]
+
+```kotlin
+fun replaceToTrue()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 替换指定 Resources 为 `true`。
+
+!> 确保目标替换 Resources 的类型为 `Boolean`。
+
+#### replaceToFalse [method]
+
+```kotlin
+fun replaceToFalse()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 替换指定 Resources 为 `false`。
+
+!> 确保目标替换 Resources 的类型为 `Boolean`。
+
+#### replaceToModuleResource [method]
+
+```kotlin
+fun replaceToModuleResource(resId: Int)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 替换为当前 Xposed 模块的 Resources。
+
+你可以直接使用模块的 `R.string.xxx`、`R.mipmap.xxx`、`R.drawable.xxx` 替换 Hook APP 的 Resources。
+
+**功能示例**
+
+使用此方法可非常方便地使用当前模块的 Resources 去替换目标 Hook APP 的 Resources。
+
+这个过程你无需对目标 Resources 实现 `fwd` 方法。
+
+比如我们要替换一个字符串。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "test_string"
+ string()
+ }
+ replaceToModuleResource(R.id.module_string)
+}
+```
+
+还可以替换一些复杂的 Resources,比如 `xml` 创建的 `drawable`。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "test_drawable"
+ drawable()
+ }
+ replaceToModuleResource(R.drawable.module_drawable)
+}
+```
+
+#### injectAsLayout [method]
+
+```kotlin
+fun injectAsLayout(initiate: YukiResources.LayoutInflatedParam.() -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 作为装载的布局注入。
+
+**功能示例**
+
+你可以直接注入一个布局监听并修改它的内部 `View`。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "activity_main"
+ layout()
+ }
+ injectAsLayout {
+ findViewByIdentifier(name = "test_view")?.isVisible = false
+ findViewByIdentifier(name = "test_text_view")?.text = "Hook this"
+ }
+}
+```
+
+你还可以通过 `currentView` 拿到 `Context`。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "activity_main"
+ layout()
+ }
+ injectAsLayout {
+ Toast.makeText(currentView.context, "Hook this", Toast.LENGTH_SHORT).show()
+ }
+}
+```
+
+#### ConditionFinder [class]
+
+```kotlin
+inner class ConditionFinder
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> Resources 查找条件实现类。
+
+##### name [field]
+
+```kotlin
+var name: String
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 名称。
+
+##### anim [method]
+
+```kotlin
+fun anim()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为动画。
+
+##### animator [method]
+
+```kotlin
+fun animator()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为属性动画。
+
+##### bool [method]
+
+```kotlin
+fun bool()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为布朗(Boolean)。
+
+##### color [method]
+
+```kotlin
+fun color()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为颜色(Color)。
+
+##### dimen [method]
+
+```kotlin
+fun dimen()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为尺寸(Dimention)。
+
+##### drawable [method]
+
+```kotlin
+fun drawable()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为 Drawable。
+
+##### integer [method]
+
+```kotlin
+fun integer()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为整型(Integer)。
+
+##### layout [method]
+
+```kotlin
+fun layout()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为布局(Layout)。
+
+##### plurals [method]
+
+```kotlin
+fun plurals()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为 Plurals。
+
+##### string [method]
+
+```kotlin
+fun string()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为字符串(String)。
+
+##### xml [method]
+
+```kotlin
+fun xml()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为 Xml。
+
+##### mipmap [method]
+
+```kotlin
+fun mipmap()
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 Resources 类型为位图(Mipmap)。
+
+#### Result [class]
+
+```kotlin
+inner class Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 监听全部 Hook 结果实现类,可在这里处理失败事件监听。
+
+##### result [method]
+
+```kotlin
+inline fun result(initiate: Result.() -> Unit): Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 创建监听事件方法体。
+
+##### by [method]
+
+```kotlin
+inline fun by(initiate: () -> Boolean): Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 添加执行 Hook 需要满足的条件,不满足条件将直接停止 Hook。
+
+#### onHookingFailure [method]
+
+```kotlin
+fun onHookingFailure(initiate: (Throwable) -> Unit): Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 监听 Hook 过程发生错误的回调方法。
+
+#### ignoredHookingFailure [method]
+
+```kotlin
+fun ignoredHookingFailure(): Result
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 忽略 Hook 过程出现的错误。
\ No newline at end of file
diff --git a/docs/api/public/YukiXposedEvent.md b/docs/api/public/YukiXposedEvent.md
new file mode 100644
index 00000000..55e25561
--- /dev/null
+++ b/docs/api/public/YukiXposedEvent.md
@@ -0,0 +1,69 @@
+## YukiXposedEvent [object]
+
+```kotlin
+object YukiXposedEvent
+```
+
+**变更记录**
+
+`v1.0.80` `添加`
+
+**功能描述**
+
+> 实现对原生 Xposed API 的装载事件监听。
+
+### events [method]
+
+```kotlin
+inline fun events(initiate: YukiXposedEvent.() -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 对 `YukiXposedEvent` 创建一个方法体。
+
+### onInitZygote [method]
+
+```kotlin
+fun onInitZygote(initiate: (StartupParam) -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 initZygote 事件监听。
+
+### onHandleLoadPackage [method]
+
+```kotlin
+fun onHandleLoadPackage(initiate: (LoadPackageParam) -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 handleLoadPackage 事件监听。
+
+### onHandleInitPackageResources [method]
+
+```kotlin
+fun onHandleInitPackageResources(initiate: (InitPackageResourcesParam) -> Unit)
+```
+
+**变更记录**
+
+`v1.0.80` `新增`
+
+**功能描述**
+
+> 设置 handleInitPackageResources 事件监听。
\ No newline at end of file
diff --git a/docs/config/api-example.md b/docs/config/api-example.md
index a136de10..3e3f32e5 100644
--- a/docs/config/api-example.md
+++ b/docs/config/api-example.md
@@ -177,6 +177,57 @@ class HookEntryClass : IYukiHookXposedInit {
}
```
+### 扩展特性
+
+如果你当前使用的 Hook Framework 支持并启用了资源钩子(Resources Hook)功能,你现在可以直接在 `encase` 中创建 Resources Hook。
+
+你完全不需要与之前在使用 Xposed API 那样区分 `initZygote`、`handleLoadPackage`、`handleInitPackageResources` 方法来执行不同的功能。
+
+在 `YukiHookAPI` 中,这些功能**是无缝的**。
+
+> 示例如下
+
+```kotlin
+encase {
+ loadApp(name = "com.example.demo") {
+ findClass(name = "$packageName.DemoClass").hook {
+ // Your code here.
+ }
+ // 创建一个 Resources Hook (固定用法)
+ resources().hook {
+ // Your code here.
+ }
+ }
+}
+```
+
+你还可以同时使用 `loadZygote` 方法来装载系统框架。
+
+> 示例如下
+
+```kotlin
+encase {
+ loadZygote {
+ ActivityClass.hook {
+ // Your code here.
+ }
+ // 在 Zygote 中创建 Resources Hook
+ resources().hook {
+ // Your code here.
+ }
+ }
+ loadApp(name = "com.example.demo") {
+ findClass(name = "$packageName.DemoClass").hook {
+ // Your code here.
+ }
+ // 在 APP 中创建 Resources Hook
+ resources().hook {
+ // Your code here.
+ }
+ }
+}
+```
+
## 作为 Hook API 使用需要注意的地方
若你作为 Hook API 使用,那么你只需要在入口处对 `encase` 方法进行区分。
@@ -196,4 +247,6 @@ fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker)
此处的 `baseContext` 只需填入你在 `attachBaseContext` 处得到的 `Context` 即可,其它用法与上述内容完全一致。
-!> 切勿以 Xposed 方式使用 `encase` 方法而漏掉 `baseContext` 参数,否则你的 Hook 将完全不工作。
\ No newline at end of file
+!> 切勿以 Xposed 方式使用 `encase` 方法而漏掉 `baseContext` 参数,否则你的 Hook 将完全不工作。
+
+!> Resources Hook 功能不支持作为 Hook API 使用。
\ No newline at end of file
diff --git a/docs/config/api-exception.md b/docs/config/api-exception.md
index b9c08495..ed600518 100644
--- a/docs/config/api-exception.md
+++ b/docs/config/api-exception.md
@@ -16,11 +16,11 @@
请确认你在正确的地方装载了 `YukiHookAPI` 的 `encase` 方法,详情请参考 [作为 Xposed 模块使用的相关配置](config/xposed-using) 以及 [作为 Hook API 使用的相关配置](config/api-using)。
-!> `loggerE` You cannot load a hooker in "onInit" method! Aborted
+!> `loggerE` You cannot load a hooker in "onInit" or "onXposedEvent" method! Aborted
**异常原因**
-你尝试在继承 `IYukiHookXposedInit` 的 Hook 入口类的 `onInit` 方法中装载了 `encase` 方法。
+你尝试在继承 `IYukiHookXposedInit` 的 Hook 入口类的 `onInit` 或 `onXposedEvent` 方法中装载了 `encase` 或 `resources` 方法。
> 示例如下
@@ -34,6 +34,13 @@ class HookEntry : IYukiHookXposedInit {
}
}
+ override fun onXposedEvent() {
+ // ❗错误的使用方法
+ YukiHookAPI.encase {
+ // Your code here.
+ }
+ }
+
override fun onHook() {
// Your code here.
}
@@ -75,6 +82,16 @@ class HookEntry : IYukiHookXposedInit {
通常情况下这种错误不会轻易发生,若一旦发生此错误,请自行查看控制台打印的日志定位问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。
+!> `loggerE` YukiHookAPI bind initZygote failed
+
+**异常原因**
+
+`YukiHookAPI` 在尝试装载 Xposed 原生接口 `initZygote` 方法时发生了不能处理的异常。
+
+**解决方案**
+
+通常情况下这种错误不会轻易发生,若一旦发生此错误,请自行查看控制台打印的日志定位问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。
+
!> `loggerE` HookClass \[**NAME**\] not found
**异常原因**
@@ -361,6 +378,50 @@ method {
请检查查询条件中 `param` 的 `index` 号下标的 `Class` 是否存在,然后再试一次。
+!> `loggerE` Resources Hook condition name/type cannot be empty \[**TAG**\]
+
+**异常原因**
+
+在查找 Resources 时并未设置任何条件。
+
+> 示例如下
+
+```kotlin
+// 情况 1
+conditions {
+ // 这里没有设置任何条件
+}
+// 情况 2
+conditions {
+ name = "test"
+ // 这里缺少了 type 条件
+}
+```
+
+**解决方案**
+
+Resources 的 Hook 并非类似方法的 Hook,其必须拥有完整的名称和类型描述才能查询成功,请将查询条件补充完整并再试一次。
+
+!> `loggerE` Resources Hook type is invalid \[**TAG**\]
+
+**异常原因**
+
+在 Hook Resources 时发生了类型错误的异常。
+
+**解决方案**
+
+`YukiHookAPI` 会尝试在 `initZygote` 与 `handleInitPackageResources` 中装载 Resources Hook,若全部装载失败可能会发生此异常,当前 Hook Framework 需要支持并启用资源钩子(Resources Hook)功能,请检查后再试一次。
+
+!> `loggerE` Resources Hook got an Exception \[**TAG**\]
+
+**异常原因**
+
+在 Hook Resources 时发生了任意的异常。
+
+**解决方案**
+
+这是一个异常汇总,请自行向下查看日志具体的异常是什么,例如找不到 Resources Id 的问题。
+
## 阻断异常
> 这些异常会直接导致 APP 停止运行(FC),同时会在控制台打印 `E` 级别的日志,还会造成 Hook 进程“死掉”。
@@ -682,6 +743,42 @@ encase {
`appContext` 在宿主环境初始化完成之前有大的概率可能是空的,请延迟获取或在宿主的 Hook 方法回调方法体内再使用此变量。
+!> `IllegalStateException` Current Hook Framework not support moduleAppResources
+
+**异常原因**
+
+在 `PackageParam` 中调用了 `moduleAppResources` 变量但是无法获取到实例对象。
+
+> 示例如下
+
+```kotlin
+encase {
+ // 调用了此变量
+ moduleAppResources...
+}
+```
+
+**解决方案**
+
+`moduleAppResources` 需要当前 Hook Framework 支持 `initZygote` 功能,请检查后再试一次。
+
+!> `IllegalStateException` You cannot call to appResources in this time
+
+在 `PackageParam` 中调用了 `appResources` 变量但是无法获取到实例对象。
+
+> 示例如下
+
+```kotlin
+encase {
+ // 调用了此变量
+ appResources...
+}
+```
+
+**解决方案**
+
+`appResources` 不会在 `initZygote` 中装载,另外,这个功能需要当前 Hook Framework 支持并启用资源钩子(Resources Hook)功能,请检查后再试一次。
+
!> `IllegalStateException` VariousClass match failed of those **CLASSES**
**异常原因**
@@ -713,6 +810,48 @@ TargetClass.hook {
详情请参考 [状态监听](guide/example?id=状态监听)。
+!> `IllegalStateException` LayoutInflatedParam View instance got null
+
+**异常原因**
+
+在布局 Hook 回调中调用了 `currentView` 但没取到实例对象。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ conditions {
+ name = "activity_main"
+ layout()
+ }
+ injectAsLayout {
+ // 调用了此变量
+ currentView...
+ }
+}
+```
+
+**解决方案**
+
+这种情况基本上不存在,除非被 Hook 的宿主当前 `Activity` 已经销毁或 Hook Framework 自身存在问题。
+
+!> `IllegalStateException` XResForwarder is invalid
+
+**异常原因**
+
+在 `YukiResForwarder` 中调用了 `resources` 但没取到实例对象。
+
+> 示例如下
+
+```kotlin
+// 调用了此变量
+moduleAppResources.fwd(...).resources
+```
+
+**解决方案**
+
+这种情况基本上不存在,除非 Hook Framework 自身存在问题。
+
!> `IllegalStateException` Hook Members is empty, hook aborted
**异常原因**
@@ -731,6 +870,24 @@ TargetClass.hook {
你必须在 `hook` 方法体内加入至少一个 `injectMember` 方法。
+!> `IllegalStateException` Hook Resources is empty, hook aborted
+
+**异常原因**
+
+使用了 `hook` 方法体但其中并没有填写内容。
+
+> 示例如下
+
+```kotlin
+resources().hook {
+ // 这里没有填写任何内容
+}
+```
+
+**解决方案**
+
+你必须在 `hook` 方法体内加入至少一个 `injectResources` 方法。
+
!> `IllegalStateException` paramTypes is empty, please use emptyParam() instead
**异常原因**
@@ -771,4 +928,14 @@ method {
name = "test"
paramCount = 0
}
-```
\ No newline at end of file
+```
+
+!> `IllegalStateException` Invalid YukiHookCallback type
+
+**异常原因**
+
+`YukiHookAPI` 的核心 Hook 功能发生故障。
+
+**解决方案**
+
+这种情况基本上不存在,若发生上述问题,确定并非自己的代码发生的问题后,可提交日志进行反馈。
\ No newline at end of file
diff --git a/docs/config/r8-proguard.md b/docs/config/r8-proguard.md
index d0753c38..f5c3c6fa 100644
--- a/docs/config/r8-proguard.md
+++ b/docs/config/r8-proguard.md
@@ -10,16 +10,7 @@
> ~~如果你仍然在使用 `Proguard`,你需要做一些规则配置。~~
-~~在 `proguard-rules.pro` 添加如下代码即可。~~
-
-> ~~示例如下~~
-
-```proguard
--keep class com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus {*;}
--keep class 这里填你的 HookEntryClass 入口类完整包名_YukiHookXposedInit {*;}
-```
-
-!> Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 `R8`,基本可以不需要考虑混淆的问题。
+!> Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 `R8`,不再需要考虑混淆的问题。
若要在任何版本下启用 `R8`,请在 `gradle.properties` 文件中加入如下规则,Android Gradle Plugin 7.0 及以上版本无需任何配置。
diff --git a/docs/config/xposed-using.md b/docs/config/xposed-using.md
index de752367..6d9de824 100644
--- a/docs/config/xposed-using.md
+++ b/docs/config/xposed-using.md
@@ -96,7 +96,7 @@ Xposed 入口类处理如下。
> 示例如下
```kotlin
-class HookEntry_YukiHookXposedInit: IXposedHookLoadPackage, ...
+class HookEntry_YukiHookXposedInit: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources
```
编译后的类名结构如下。
@@ -105,6 +105,7 @@ class HookEntry_YukiHookXposedInit: IXposedHookLoadPackage, ...
```
...hook.HookEntry ← 你的入口类
+...hook.HookEntry_Impl ← 自动生成的 Impl 类
...hook.HookEntry_YukiHookXposedInit ← 自动生成的 Xposed 入口类
```
@@ -122,7 +123,7 @@ Xposed 入口类处理如下。
> 示例如下
```kotlin
-class HookXposedEntry: IXposedHookLoadPackage, ...
+class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources
```
编译后的类名结构如下。
@@ -131,6 +132,7 @@ class HookXposedEntry: IXposedHookLoadPackage, ...
```
...hook.HookEntry ← 你的入口类
+...hook.HookEntry_Impl ← 自动生成的 Impl 类
...hook.HookXposedEntry ← 自动生成的 Xposed 入口类
```
@@ -138,21 +140,47 @@ class HookXposedEntry: IXposedHookLoadPackage, ...
### IYukiHookXposedInit 接口
-```kotlin
-interface IYukiHookXposedInit {
-
- fun onInit()
-
- fun onHook()
-}
-```
-
`IYukiHookXposedInit` 接口为你的 `HookEntryClass` 必须实现的接口,这是你的模块开始 Hook 的起点。
若要了解更多可 [点击这里](api/document?id=iyukihookxposedinit-interface) 进行查看。
当你的模块被 Xposed 装载后,`onHook` 方法将会被回调,你需要在此方法中开始使用 `YukiHookAPI`。
-> 基本的调用流程为 `YukiHookInjectXposedInitClass.handleLoadPackage` → `HookEntryClass.onInit` → `HookEntryClass.onHook` → `YukiHookAPI.onXposedLoaded`
+> 基本的调用流程为 `_YukiHookXposedInit` → `IYukiHookXposedInit.onXposedEvent` → `IYukiHookXposedInit.onInit` → `IYukiHookXposedInit.onHook`
-详情请参考 [API 基本配置](config/api-example)。
\ No newline at end of file
+详情请参考 [API 基本配置](config/api-example)。
+
+## 原生 Xposed API 事件
+
+若你当前的 Xposed 模块使用了第三方的资源,但是短时间内可能无法转移它们,此时,你可以使用 `onXposedEvent` 实现监听原生 Xposed API 的全部装载事件。
+
+> 示例如下
+
+```kotlin
+@InjectYukiHookWithXposed
+class HookEntry: IYukiHookXposedInit {
+
+ override fun onHook() {
+ // Your code here.
+ }
+
+ override fun onXposedEvent() {
+ // 监听原生 Xposed API 的装载事件
+ YukiXposedEvent.events {
+ onInitZygote {
+ // it 对象即 [StartupParam]
+ }
+ onHandleLoadPackage {
+ // it 对象即 [LoadPackageParam]
+ }
+ onHandleInitPackageResources {
+ // it 对象即 [InitPackageResourcesParam]
+ }
+ }
+ }
+}
+```
+
+`onXposedEvent` 与 `onHook` 方法完全独立存在,互不影响,你可以继续在 `onHook` 方法中使用 `YukiHookAPI`。
+
+若要了解更多可 [点击这里](api/document?id=onxposedevent-method) 进行查看。
\ No newline at end of file
diff --git a/docs/guide/example.md b/docs/guide/example.md
index e9042051..45ead4cb 100644
--- a/docs/guide/example.md
+++ b/docs/guide/example.md
@@ -8,7 +8,7 @@
```
Host Environment
-└── YukiHookCreater
+└── YukiMemberHookCreater
└── Class
└── MemberHookCreater
└── Member
@@ -19,6 +19,15 @@ Host Environment
├── Before
└── After
...
+ YukiResourcesHookCreater
+ └── Resources
+ └── ResourcesHookCreater
+ └── Drawable
+ └── Replace
+ ResourcesHookCreater
+ └── Layout
+ └── Inject
+ ...
```
> 上方的结构换做代码将可写为如下形式。
@@ -37,6 +46,14 @@ TargetClass.hook {
}
}
}
+resources().hook {
+ injectResource {
+ conditions {
+ // Your code here.
+ }
+ replaceTo(...)
+ }
+}
```
## Demo
@@ -51,6 +68,10 @@ TargetClass.hook {
## 一个简单的 Hook 例子
+> 这里给出了 Hook APP、Hook 系统框架与 Hook Resources 的例子,可供参考。
+
+### Hook APP
+
假设,我们要 Hook `com.android.browser` 中的 `onCreate` 方法并弹出一个对话框。
在 `encase` 方法体中添加代码。
@@ -171,6 +192,97 @@ TestClass.hook {
}
```
+### Hook 系统框架
+
+在 `YukiHookAPI` 中,Hook 系统框架的实现非常简单。
+
+假设我们要全局 Hook 一个 `Activity` 的 `onCreate` 事件
+
+在 `encase` 方法体中添加代码。
+
+> 示例如下
+
+```kotlin
+loadZygote {
+ ActivityClass.hook {
+ injectMember {
+ method {
+ name = "onCreate"
+ param(BundleClass)
+ returnType = UnitType
+ }
+ afterHook {
+ // Your code here.
+ }
+ }
+ }
+}
+```
+
+这样就实现了上述的 Hook 功能。
+
+!> `loadZygote` 与 `loadApp(name = "android")` 有直接性区别,`loadZygote` 会在 `initZygote` 中装载,若要 Hook 系统框架,建议使用 `loadZygote`。
+
+### Hook Resources
+
+假设,我们要 Hook `com.android.browser` 中 `string` 类型的 `app_name` 内容替换为 `123`。
+
+在 `encase` 方法体中添加代码。
+
+> 示例如下
+
+```kotlin
+loadApp(name = "com.android.browser") {
+ resources().hook {
+ injectResource {
+ conditions {
+ name = "app_name"
+ string()
+ }
+ replaceTo("123")
+ }
+ }
+}
+```
+
+若当前 APP 使用 `app_name` 设置了标题栏文本,则它就会变成我们的 `123`。
+
+你还可以使用当前 Xposed 模块的 Resources 替换 Hook APP 的 Resources。
+
+假设,我们要继续 Hook `com.android.browser` 中 `mipmap` 类型的 `ic_launcher`。
+
+> 示例如下
+
+```kotlin
+loadApp(name = "com.android.browser") {
+ resources().hook {
+ injectResource {
+ conditions {
+ name = "ic_launcher"
+ mipmap()
+ }
+ replaceToModuleResource(R.mipmap.ic_launcher)
+ }
+ }
+}
+```
+
+至此目标 APP 的图标将会被替换为我们设置的图标。
+
+若你想替换系统框架的资源,同样也可以这样实现,只需要把 `loadApp` 换成 `loadZygote` 即可。
+
+> 示例如下
+
+```kotlin
+loadZygote {
+ resources().hook {
+ // Your code here.
+ }
+}
+```
+
+更多功能请参考 [ResourcesHookCreater](api/document?id=resourceshookcreater-class)。
+
## 异常处理
> `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
@@ -193,6 +305,20 @@ injectMember {
}
```
+在 Resources Hook 时此方法同样适用。
+
+> 示例如下
+
+```kotlin
+injectResource {
+ // Your code here.
+}.result {
+ // 处理 Hook 时的任意异常
+ onHookingFailure {}
+ // ...
+}
+```
+
你还可以处理 Hook 的 `Class` 不存在时发生的异常。
> 示例如下
diff --git a/docs/guide/home.md b/docs/guide/home.md
index 316f7087..97779d5f 100644
--- a/docs/guide/home.md
+++ b/docs/guide/home.md
@@ -62,6 +62,8 @@
现在,我们只需要编写少量的代码,一切时间开销和花费交给自动化处理。
+借助 `Kotlin` 优雅的 `lambda` 写法以及 `YukiHookAPI`,可以让你的 Hook 逻辑更加美观清晰。
+
> 示例如下
@@ -70,9 +72,34 @@
```kotlin
@InjectYukiHookWithXposed
-class MainHook : IYukiHookXposedInit {
+class HookEntry : IYukiHookXposedInit {
override fun onHook() = encase {
+ loadZygote {
+ ActivityClass.hook {
+ injectMember {
+ method {
+ name = "onCreate"
+ param(BundleClass)
+ }
+ beforeHook {
+ // Your code here.
+ }
+ afterHook {
+ // Your code here.
+ }
+ }
+ }
+ resources().hook {
+ injectResource {
+ conditions {
+ name = "sym_def_app_icon"
+ mipmap()
+ }
+ replaceToModuleResource(R.mipmap.ic_launcher)
+ }
+ }
+ }
loadApp(name = "com.android.browser") {
ActivityClass.hook {
injectMember {
@@ -88,6 +115,15 @@ class MainHook : IYukiHookXposedInit {
}
}
}
+ resources().hook {
+ injectResource {
+ conditions {
+ name = "ic_launcher"
+ mipmap()
+ }
+ replaceToModuleResource(R.mipmap.ic_launcher)
+ }
+ }
}
}
}
@@ -96,14 +132,16 @@ class MainHook : IYukiHookXposedInit {
#### **Xposed API**
```kotlin
-class MainHook : IXposedHookLoadPackage {
+class HookEntry : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
- override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
- if (lpparam.packageName == "com.android.browser")
- XposedHelpers.findAndHookMethod(
+ private lateinit var moduleResources: XModuleResources
+
+ override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
+ moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
+ XResources.setSystemWideReplacement("android", "mipmap", "sym_def_app_icon", moduleResources.fwd(R.mipmap.ic_launcher))
+ XposedHelpers.findAndHookMethod(
Activity::class.java.name,
- lpparam.classLoader,
- "onCreate",
+ null, "onCreate",
Bundle::class.java,
object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam?) {
@@ -115,14 +153,36 @@ class MainHook : IXposedHookLoadPackage {
}
})
}
+
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName == "com.android.browser")
+ XposedHelpers.findAndHookMethod(
+ Activity::class.java.name,
+ lpparam.classLoader, "onCreate",
+ Bundle::class.java,
+ object : XC_MethodHook() {
+ override fun beforeHookedMethod(param: MethodHookParam?) {
+ // Your code here.
+ }
+
+ override fun afterHookedMethod(param: MethodHookParam?) {
+ // Your code here.
+ }
+ })
+ }
+
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName == "com.android.browser")
+ resparam.res.setReplacement("com.android.browser", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
+ }
}
```
-是的,你没有看错,仅仅就需要这几行代码,就一切安排妥当。
+是的,你没有看错,仅仅就需要这些代码,就能完全取代 Xposed API 实现同样的功能。
-代码量少,逻辑清晰,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
+现在,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
## 支持的 Hook 框架
@@ -131,7 +191,7 @@ class MainHook : IXposedHookLoadPackage {
| Hook Framework | ST | Describe |
| --------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------- |
| [LSPosed](https://github.com/LSPosed/LSPosed) | ✅ | 多场景下稳定使用 |
-| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ✅ | 部分兼容 |
+| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ❎ | 已停止维护,不再推荐使用 |
| [Pine](https://github.com/canyie/pine) | ⭕ | 可以使用 |
| [SandHook](https://github.com/asLody/SandHook) | ⭕ | 可以使用 |
| [Whale](https://github.com/asLody/whale) | ⭕ | 需要 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale) |
diff --git a/docs/guide/move-to-new-api.md b/docs/guide/move-to-new-api.md
index 775c3742..7feac1ae 100644
--- a/docs/guide/move-to-new-api.md
+++ b/docs/guide/move-to-new-api.md
@@ -22,15 +22,28 @@ override fun onHook() = encase {
appInfo
// 得到宿主 Application 生命周期
appContext
- // 创建 Hook
- findClass("com.demo.Test").hook {
- injectMember {
- method {
- name = "test"
- param(BooleanType)
+ // Hook 指定的 APP
+ loadApp(name = "com.demo.test") {
+ // Class Hook
+ findClass("com.demo.test.TestClass").hook {
+ injectMember {
+ method {
+ name = "test"
+ param(BooleanType)
+ }
+ afterHook {
+ // ...
+ }
}
- afterHook {
- // ...
+ }
+ // Resources Hook (固定用法)
+ resources().hook {
+ injectResource {
+ conditions {
+ name = "ic_launcher"
+ mipmap()
+ }
+ replaceToModuleResource(R.mipmap.ic_launcher)
}
}
}
@@ -40,6 +53,12 @@ override fun onHook() = encase {
#### **Xposed API**
```kotlin
+private lateinit var moduleResources: XModuleResources
+
+override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
+ moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
+}
+
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
// 得到当前 Hook 的包名
lpparam.packageName
@@ -47,12 +66,20 @@ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
lpparam.applicationInfo
// 得到宿主 Application 生命周期
AndroidAppHelper.currentApplication()
+ // Class Hook
+ if(lpparam.packageName == "com.demo.test")
+ XposedHelpers.findAndHookMethod("com.demo.test.TestClass", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ // ...
+ }
+ })
+}
+
+override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ // 得到当前 Hook 的包名
+ resparam.packageName
// 创建 Hook
- XposedHelpers.findAndHookMethod("com.demo.Test", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() {
- override fun afterHookedMethod(param: MethodHookParam) {
- // ...
- }
- })
+ resparam.res.setReplacement("com.demo.test", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
}
```
diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md
index eac71914..27b8377a 100644
--- a/docs/guide/quick-start.md
+++ b/docs/guide/quick-start.md
@@ -101,7 +101,7 @@ dependencies {
```kotlin
@InjectYukiHookWithXposed
-class MainHook : IYukiHookXposedInit {
+class HookEntry : IYukiHookXposedInit {
override fun onHook() = YukiHookAPI.encase {
// Your code here.
@@ -153,4 +153,4 @@ override fun attachBaseContext(base: Context?) {
### 特别说明
-!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 功能将失效。
\ No newline at end of file
+!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 以及 Resources Hook 功能将失效。
\ No newline at end of file
diff --git a/docs/guide/special-feature.md b/docs/guide/special-feature.md
index 32859e15..a82f3d4c 100644
--- a/docs/guide/special-feature.md
+++ b/docs/guide/special-feature.md
@@ -11,7 +11,26 @@
```java
package com.demo;
-public class Test {
+public class BaseTest {
+
+ public BaseTest() {
+ // ...
+ }
+
+ public BaseTest(boolean isInit) {
+ // ...
+ }
+
+ private void doBaseTask(String taskName) {
+ // ...
+ }
+}
+```
+
+```java
+package com.demo;
+
+public class Test extends BaseTest {
public Test() {
// ...
@@ -61,7 +80,7 @@ public class Test {
### 查询与反射调用
-假设我们要得到 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。
+假设我们要得到 `Test`(以下统称“当前 `Class`”)的 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。
> 示例如下
@@ -178,6 +197,54 @@ Test::class.java.method {
}.get(instance) // 得到这个方法
```
+### 在父类查询
+
+你会注意到 `Test` 继承于 `BaseTest`,现在我们想得到 `BaseTest` 的 `doBaseTask` 方法,在不知道父类名称的情况下,要怎么做呢?
+
+参照上面的查询条件,我们只需要在查询条件中加入一个 `superClass` 即可实现这个功能。
+
+> 示例如下
+
+```kotlin
+// 假设这就是这个 Class 的实例
+val instance = Test()
+// 使用 YukiHookAPI 调用并执行
+Test::class.java.method {
+ name = "doBaseTask"
+ param(StringType)
+ // 只需要添加这个条件
+ superClass()
+}.get(instance).call("task_name")
+```
+
+这个时候我们就可以在父类中取到这个方法了。
+
+`superClass` 有一个参数为 `isOnlySuperClass`,设置为 `true` 后,可以跳过当前 `Class` 仅查询当前 `Class` 的父类。
+
+由于我们现在已知 `doBaseTask` 方法只存在于父类,可以加上这个条件节省查询时间。
+
+> 示例如下
+
+```kotlin
+// 假设这就是这个 Class 的实例
+val instance = Test()
+// 使用 YukiHookAPI 调用并执行
+Test::class.java.method {
+ name = "doBaseTask"
+ param(StringType)
+ // 加入一个查询条件
+ superClass(isOnlySuperClass = true)
+}.get(instance).call("task_name")
+```
+
+这个时候我们同样可以得到父类中的这个方法。
+
+`superClass` 一旦设置就会自动循环向后查找全部继承的父类中是否有这个方法,直到查询到目标没有父类(继承关系为 `java.lang.Object`)为止。
+
+更多用法可参考 [superClass 方法](api/document?id=superclass-method)。
+
+!> 当前查询的 `Method` 除非指定 `superClass` 条件,否则只能查询到当前 `Class` 的 `Method`。
+
### 静态字节码
有些方法和变量在 `Class` 中是静态的实现,这个时候,我们不需要传入实例就可以调用它们。
@@ -347,6 +414,23 @@ instance.current {
}
```
+我们还可以用 `superClass` 调用当前 `Class` 父类的方法。
+
+> 示例如下
+
+```kotlin
+// 假设这就是这个 Class 的实例
+val instance = Test()
+// 假设这个 Class 是不能被直接得到的
+instance.current {
+ // 执行父类的 doBaseTask 方法
+ superClass().method {
+ name = "doBaseTask"
+ param(StringType)
+ }.call("task_name")
+}
+```
+
问题又来了,我想使用反射的方式创建如下的实例并调用其中的方法,该怎么做呢?
> 示例如下
@@ -768,9 +852,9 @@ loggerE(msg = "This is an error")
```kotlin
// 假设这就是被抛出的异常
-val e = Throwable(...)
+val throwable = Throwable(...)
// 打印日志
-loggerE(msg = "This is an error", e = e)
+loggerE(msg = "This is an error", e = throwable)
```
打印的结果为如下所示。
diff --git a/docs/index.html b/docs/index.html
index 8205148d..b2c46f49 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -44,7 +44,7 @@
coverpage: true,
loadNavbar: true,
loadSidebar: true,
- subMaxLevel: 3,
+ subMaxLevel: 4,
auto2top: true,
mergeNavbar: false,
notFoundPage: '_404.md',
@@ -60,7 +60,7 @@
],
placeholder: '搜索文档',
noData: '噫,什么都没找到~',
- depth: 5,
+ depth: 7,
hideOtherSidebarContent: true
},
tabs: {
diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt
index b42b50f6..f6c0105e 100644
--- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt
+++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/sources/CodeSourceFileTemplate.kt
@@ -31,13 +31,16 @@ import java.text.SimpleDateFormat
import java.util.*
/**
- * 代码文件注入器
+ * 代码文件注入模板类
*/
object CodeSourceFileTemplate {
/** 定义 Jvm 方法名 */
private const val IS_ACTIVE_METHOD_NAME = "__--"
+ /** 定义 Jvm 方法名 */
+ private const val HAS_RESOURCES_HOOK_METHOD_NAME = "_--_"
+
/** 定义 Jvm 方法名 */
private const val GET_XPOSED_VERSION_METHOD_NAME = "--__"
@@ -92,80 +95,150 @@ object CodeSourceFileTemplate {
/**
* 获得 xposed_init 注入文件
* @param packageName 包名
- * @param modulePackageName 模块包名
* @param entryClassName 入口类名
* @param xInitClassName xposed_init 入口类名
* @return [ByteArray]
*/
- fun getXposedInitFileByteArray(packageName: String, modulePackageName: String, entryClassName: String, xInitClassName: String) =
- ("package $packageName\n" +
+ fun getXposedInitFileByteArray(packageName: String, entryClassName: String, xInitClassName: String) =
+ ("@file:Suppress(\"ClassName\")\n" +
+ "\n" +
+ "package $packageName\n" +
"\n" +
"import androidx.annotation.Keep\n" +
- "import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookXposedBridge\n" +
- "import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus\n" +
- "import com.highcapable.yukihookapi.hook.log.loggerE\n" +
- "import de.robv.android.xposed.IXposedHookLoadPackage\n" +
- "import de.robv.android.xposed.XC_MethodReplacement\n" +
- "import de.robv.android.xposed.XposedHelpers\n" +
- "import de.robv.android.xposed.XposedBridge\n" +
- "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
+ "import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" +
"import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
- "import $packageName.$entryClassName\n" +
+ "import de.robv.android.xposed.IXposedHookInitPackageResources\n" +
+ "import de.robv.android.xposed.IXposedHookLoadPackage\n" +
+ "import de.robv.android.xposed.IXposedHookZygoteInit\n" +
+ "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" +
+ "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
"\n" +
- getCommentContent(entryClassName, currrentClassTag = "XposedInit") +
+ getCommentContent(entryClassName, currrentClassTag = "Xposed Init") +
"@Keep\n" +
"@YukiGenerateApi\n" +
- "class $xInitClassName : IXposedHookLoadPackage {\n" +
+ "class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {\n" +
+ "\n" +
+ " override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
+ " ${entryClassName}_Impl.callInitZygote(sparam)\n" +
+ " YukiXposedEvent.EventHandler.callInitZygote(sparam)\n" +
+ " }\n" +
"\n" +
" override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
- " if (lpparam == null) return\n" +
- " try {\n" +
- " $entryClassName().apply {\n" +
- " onInit()\n" +
- " if (YukiHookXposedBridge.isXposedCallbackSetUp) {\n" +
- " loggerE(tag = \"YukiHookAPI\", msg = \"You cannot loading a hooker in \\\"onInit\\\" method! Aborted\")\n" +
- " return\n" +
- " }\n" +
- " onHook()\n" +
+ " ${entryClassName}_Impl.callHandleLoadPackage(lpparam)\n" +
+ " YukiXposedEvent.EventHandler.callHandleLoadPackage(lpparam)\n" +
+ " }\n" +
+ "\n" +
+ " override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
+ " ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" +
+ " YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" +
+ " }\n" +
+ "}").toByteArray()
+
+ /**
+ * 获得 xposed_init_Impl 注入文件
+ * @param packageName 包名
+ * @param modulePackageName 模块包名
+ * @param entryClassName 入口类名
+ * @return [ByteArray]
+ */
+ fun getXposedInitImplFileByteArray(packageName: String, modulePackageName: String, entryClassName: String) =
+ ("@file:Suppress(\"ClassName\")\n" +
+ "\n" +
+ "package $packageName\n" +
+ "\n" +
+ "import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
+ "import com.highcapable.yukihookapi.hook.log.loggerE\n" +
+ "import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus\n" +
+ "import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge\n" +
+ "import de.robv.android.xposed.IXposedHookZygoteInit\n" +
+ "import de.robv.android.xposed.XC_MethodReplacement\n" +
+ "import de.robv.android.xposed.XposedHelpers\n" +
+ "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" +
+ "import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
+ "\n" +
+ getCommentContent(entryClassName, currrentClassTag = "Xposed Init Impl") +
+ "@YukiGenerateApi\n" +
+ "object ${entryClassName}_Impl {\n" +
+ "\n" +
+ " private const val modulePackageName = \"$modulePackageName\"\n" +
+ " private val hookEntry = $entryClassName()\n" +
+ " private var isZygoteBinded = false\n" +
+ " private var lpparam: XC_LoadPackage.LoadPackageParam? = null\n" +
+ "\n" +
+ " private fun callXposedLoaded(\n" +
+ " isZygoteLoaded: Boolean = false,\n" +
+ " lpparam: XC_LoadPackage.LoadPackageParam? = null,\n" +
+ " resparam: XC_InitPackageResources.InitPackageResourcesParam? = null\n" +
+ " ) {\n" +
+ " if (isZygoteBinded.not()) runCatching {\n" +
+ " hookEntry.onXposedEvent()\n" +
+ " hookEntry.onInit()\n" +
+ " if (YukiHookBridge.isXposedCallbackSetUp) {\n" +
+ " loggerE(tag = \"YukiHookAPI\", msg = \"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" +
+ " return\n" +
" }\n" +
- " YukiHookXposedBridge.callXposedInitialized()\n" +
- " } catch (e: Throwable) {\n" +
- " loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI try to load HookEntryClass failed\", e = e)\n" +
- " }\n" +
- " if (lpparam.packageName == \"$modulePackageName\") {\n" +
+ " hookEntry.onHook()\n" +
+ " YukiHookBridge.callXposedInitialized()\n" +
+ " YukiHookBridge.modulePackageName = modulePackageName\n" +
+ " }.onFailure { loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI try to load HookEntryClass failed\", e = it) }\n" +
+ " YukiHookBridge.callXposedLoaded(isZygoteLoaded, lpparam, resparam)\n" +
+ " }\n" +
+ "\n" +
+ " private fun hookModuleAppStatus(lpparam: XC_LoadPackage.LoadPackageParam? = this.lpparam, isHookResourcesStatus: Boolean = false) {\n" +
+ " lpparam?.let { this.lpparam = it }\n" +
+ " if (isHookResourcesStatus.not()) {\n" +
" XposedHelpers.findAndHookMethod(\n" +
" YukiHookModuleStatus::class.java.name,\n" +
- " lpparam.classLoader,\n" +
+ " this.lpparam?.classLoader,\n" +
" \"$IS_ACTIVE_METHOD_NAME\",\n" +
" object : XC_MethodReplacement() {\n" +
" override fun replaceHookedMethod(param: MethodHookParam?) = true\n" +
" })\n" +
" XposedHelpers.findAndHookMethod(\n" +
" YukiHookModuleStatus::class.java.name,\n" +
- " lpparam.classLoader,\n" +
+ " this.lpparam?.classLoader,\n" +
" \"$GET_XPOSED_TAG_METHOD_NAME\",\n" +
" object : XC_MethodReplacement() {\n" +
- " override fun replaceHookedMethod(param: MethodHookParam?) = try {\n" +
- " XposedBridge::class.java.getDeclaredField(\"TAG\").apply { isAccessible = true }.get(null) as String\n" +
- " } catch (_: Throwable) {\n" +
- " \"invalid\"\n" +
- " }\n" +
+ " override fun replaceHookedMethod(param: MethodHookParam?) = YukiHookBridge.executorName\n" +
" })\n" +
" XposedHelpers.findAndHookMethod(\n" +
" YukiHookModuleStatus::class.java.name,\n" +
- " lpparam.classLoader,\n" +
+ " this.lpparam?.classLoader,\n" +
" \"$GET_XPOSED_VERSION_METHOD_NAME\",\n" +
" object : XC_MethodReplacement() {\n" +
- " override fun replaceHookedMethod(param: MethodHookParam?) = try {\n" +
- " XposedBridge.getXposedVersion()\n" +
- " } catch (_: Throwable) {\n" +
- " -1\n" +
- " }\n" +
+ " override fun replaceHookedMethod(param: MethodHookParam?) = YukiHookBridge.executorVersion\n" +
" })\n" +
- " YukiHookXposedBridge.isModulePackageXposedEnv = true\n" +
- " }\n" +
- " YukiHookXposedBridge.modulePackageName = \"$modulePackageName\"\n" +
- " YukiHookXposedBridge.callXposedLoaded(lpparam)\n" +
+ " } else XposedHelpers.findAndHookMethod(\n" +
+ " YukiHookModuleStatus::class.java.name,\n" +
+ " this.lpparam?.classLoader,\n" +
+ " \"$HAS_RESOURCES_HOOK_METHOD_NAME\",\n" +
+ " object : XC_MethodReplacement() {\n" +
+ " override fun replaceHookedMethod(param: MethodHookParam?) = true\n" +
+ " })\n" +
+ " }\n" +
+ "\n" +
+ " @YukiGenerateApi\n" +
+ " fun callInitZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
+ " if (sparam == null) return\n" +
+ " runCatching {\n" +
+ " YukiHookBridge.callXposedZygoteLoaded(sparam)\n" +
+ " }.onFailure { loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI bind initZygote failed\", e = it) }\n" +
+ " callXposedLoaded(isZygoteLoaded = true)\n" +
+ " isZygoteBinded = true\n" +
+ " }\n" +
+ "\n" +
+ " @YukiGenerateApi\n" +
+ " fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
+ " if (lpparam == null) return\n" +
+ " if (lpparam.packageName == modulePackageName) hookModuleAppStatus(lpparam)\n" +
+ " callXposedLoaded(lpparam = lpparam)\n" +
+ " }\n" +
+ "\n" +
+ " @YukiGenerateApi\n" +
+ " fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
+ " if (resparam == null) return\n" +
+ " if (resparam.packageName == modulePackageName) hookModuleAppStatus(isHookResourcesStatus = true)\n" +
+ " callXposedLoaded(resparam = resparam)\n" +
" }\n" +
"}").toByteArray()
}
\ No newline at end of file