mirror of
https://github.com/HighCapable/KavaRef.git
synced 2025-09-10 04:24:12 +08:00
Initial commit
This commit is contained in:
27
docs-source/src/en/about/about.md
Normal file
27
docs-source/src/en/about/about.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# About This Document
|
||||
|
||||
> This document is powered by [VuePress](https://v2.vuepress.vuejs.org/en).
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](https://github.com/HighCapable/KavaRef/blob/main/LICENSE)
|
||||
|
||||
```:no-line-numbers
|
||||
Apache License Version 2.0
|
||||
|
||||
Copyright (C) 2019 HighCapable
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
Copyright © 2019 HighCapable
|
29
docs-source/src/en/about/changelog.md
Normal file
29
docs-source/src/en/about/changelog.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
> The version update history of `KavaRef` is recorded here.
|
||||
|
||||
::: danger
|
||||
|
||||
We will only maintain the latest API version, if you are using an outdate API version, you voluntarily renounce any possibility of maintenance.
|
||||
|
||||
:::
|
||||
|
||||
::: warning
|
||||
|
||||
To avoid translation time consumption, Changelog will use **Google Translation** from **Chinese** to **English**, please refer to the original text for actual reference.
|
||||
|
||||
Time zone of version release date: **UTC+8**
|
||||
|
||||
:::
|
||||
|
||||
## kavaref-core
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
||||
|
||||
## kavaref-extension
|
||||
|
||||
### 1.0.0 | 2025.06.25  <Badge type="tip" text="latest" vertical="middle" />
|
||||
|
||||
- The first version is submitted to Maven
|
16
docs-source/src/en/about/contacts.md
Normal file
16
docs-source/src/en/about/contacts.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Contact Us
|
||||
|
||||
> If you have any questions in use, or have any constructive suggestions, you can contact us.
|
||||
|
||||
Join our developers group.
|
||||
|
||||
- [Click to join Telegram group](https://t.me/KavaRef)
|
||||
- [Click to join Telegram group (Developer))](https://t.me/HighCapable_Dev)
|
||||
|
||||
Find me on **Twitter** [@fankesyooni](https://twitter.com/fankesyooni).
|
||||
|
||||
## Help with Maintenance
|
||||
|
||||
Thank you for choosing and using `KavaRef`.
|
||||
|
||||
If you have code-related suggestions and requests, you can submit a Pull Request on GitHub.
|
114
docs-source/src/en/about/future.md
Normal file
114
docs-source/src/en/about/future.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Looking for Future
|
||||
|
||||
> The future is bright and uncertain, let us look forward to the future development space of `KavaRef`.
|
||||
|
||||
## Future Plans
|
||||
|
||||
> Features that `KavaRef` may add later are included here.
|
||||
|
||||
### Supports Class Filtering through ClassLoader
|
||||
|
||||
`KavaRef` currently only supports search and calls for reflection APIs such as `Method`, `Field`, and `Constructor`.
|
||||
In the future, the ability to filter `Class` by specified type `ClassLoader` may be supported in Java and Android platforms according to requirements.
|
||||
|
||||
Currently, you can use [DexKit](https://github.com/LuckyPray/DexKit) to complete this requirement,
|
||||
which also supports more complex searches and calls of reflective APIs such as Method, Field, and Constructor.
|
||||
|
||||
### Automatically Generate Reflection Code
|
||||
|
||||
**This is a feature that has been initially established in [YukiReflection](https://github.com/HighCapable/YukiReflection), and `KavaRef` is ready to continue to implement it in the possible time in the future.**
|
||||
|
||||
Use `stub` to create a Kotlin class, and declare the parameters in it, as well as its different states in each version.
|
||||
|
||||
For example, the Java class below is the target class we need to reflect.
|
||||
|
||||
> The following example
|
||||
|
||||
```java:no-line-numbers
|
||||
package com.example.test;
|
||||
|
||||
public class MyClass {
|
||||
|
||||
private String myField = "test";
|
||||
|
||||
public MyClass() {
|
||||
//...
|
||||
}
|
||||
|
||||
private String myMethod1(String var1, int var2) {
|
||||
//...
|
||||
}
|
||||
|
||||
private void myMethod2() {
|
||||
//...
|
||||
}
|
||||
|
||||
private void myMethod3(String var1) {
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Through the existing usage of the current API, this class can be called reflectively in the following way.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass().resolve().apply {
|
||||
// Call myField.
|
||||
val value = firstField { name = "myField" }.get<String>()
|
||||
// Call myMethod1.
|
||||
val methodValue = firstMethod { name = "myMethod1" }.invoke<String>("test", 0)
|
||||
// Call myMethod2.
|
||||
firstMethod { name = "myMethod2" }.invoke()
|
||||
// Call myMethod3.
|
||||
firstMethod { name = "myMethod3" }.invoke("test")
|
||||
}
|
||||
```
|
||||
|
||||
The function to be implemented at present can be directly defined as the following Kotlin class using the reflection function.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
package com.example.test
|
||||
|
||||
@ReflectClass
|
||||
class MyClass {
|
||||
|
||||
@ReflectField
|
||||
val myField: String = fieldValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod1(var1: String, var2: Int): String = methodReturnValueOf("none")
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod2() = MethodReturnType.Unit
|
||||
|
||||
@ReflectMethod
|
||||
fun myMethod3(var1: String) = MethodReturnType.Unit
|
||||
}
|
||||
```
|
||||
|
||||
Then we can directly call this defined Kotlin class to implement the reflection function, and the API will automatically generate the reflection code according to the annotation.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass().also {
|
||||
// Call myField
|
||||
val value = it.myField
|
||||
// Call myMethod1
|
||||
val methodValue = it.myMethod1("test", 0)
|
||||
// Call myMethod2
|
||||
it.myMethod2()
|
||||
// Call myMethod3
|
||||
it.myMethod3("test")
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
The above functions may change after the actual release, and the functions of the actual version shall prevail.
|
||||
|
||||
:::
|
266
docs-source/src/en/config/migration.md
Normal file
266
docs-source/src/en/config/migration.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Migration to KavaRef
|
||||
|
||||
If you are used to using the reflection API in [YukiReflection](https://github.com/HighCapable/YukiReflection) or [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), you can refer to the following to migrate to `KavaRef`.
|
||||
|
||||
::: warning
|
||||
|
||||
For `YukiHookAPI`, you need to continue using its Hook API, and `KavaRef` only includes Java reflection-related APIs.
|
||||
|
||||
:::
|
||||
|
||||
## Basic Functions
|
||||
|
||||
The design concept of `KavaRef` is similar to `YukiReflection`, but not exactly the same.
|
||||
The following lists the differences between `YukiReflection` and `KavaRef` in basic reflection functions, which you can manually migrate based on.
|
||||
|
||||
For example, we have the following Java class.
|
||||
|
||||
> The following example
|
||||
|
||||
```java :no-line-numbers
|
||||
public class MyClass {
|
||||
|
||||
private void myMethod(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is a comparison of `KavaRef` with `YukiReflection` using examples.
|
||||
|
||||
> The following example
|
||||
|
||||
<div style="display: flex; gap: 16px;">
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>KavaRef</h4>
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Call and execute using KavaRef.
|
||||
MyClass::class.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.of(myClass).invoke("Hello, KavaRef!")
|
||||
// Direct reference to instance.
|
||||
myClass.resolve().firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<div style="flex: 1;">
|
||||
<h4>YukiReflection</h4>
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Call and execute using YukiReflection.
|
||||
MyClass::class.java.method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.get(myClass).call("Hello, YukiReflection!")
|
||||
// Direct reference to instance.
|
||||
myClass.current().method {
|
||||
name = "myMethod"
|
||||
param(StringClass)
|
||||
}.call("Hello, YukiReflection!")
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`KavaRef` starts reflection at any time, you need to use `resolve()` to create a reflection scope.
|
||||
You no longer directly extend the related `method` and `constructor` methods to avoid contaminating their scope.
|
||||
|
||||
`KavaRef` abandons the "Finder" design concept and uses the "Filter" design concept to obtain reflected results.
|
||||
"Find" is no longer a finding, but a "filtering".
|
||||
|
||||
`KavaRef` canceled the `YukiReflection` defined in the resulting instance whether the `Member` obtained is a multiple or a single design scheme,
|
||||
and directly returns the entire `List<MemberResolver>`.
|
||||
The example you see above uses `firstMethod` to get the first match `MethodResolver`,
|
||||
if you need to get all matches, you can change to `method`.
|
||||
|
||||
The conditional method name of `KavaRef` in `MethodCondition` has been modified from abbreviation
|
||||
such as `param` before `YukiReflection` to `parameters` to more in line with the naming habit of Java reflection API.
|
||||
|
||||
`KavaRef` no longer provides the `param(...).order()` function in the condition, because this function itself is unstable.
|
||||
`KavaRef` now uses an iterator for filtering, and the bytecode will no longer be in order, and the bytecode should not be filtered in order.
|
||||
You can use `firstMethod`, `firstField`, or `lastMethod`, `lastField`, etc. to get the first or last match result.
|
||||
|
||||
`KavaRef` renames the `get(instance)` method to `of(instance)` because `get(...)` may be confused with the `get(...)` usage of `Field` and is not semantic,
|
||||
At the same time, `get(instance)` is no longer getting the `MethodFinder.Result.Instance` instance from something like `MethodFinder.Result`,
|
||||
but uses `of(instance)` to always operate and set the instance object to `MemberResolver`.
|
||||
|
||||
Methods such as `string()`, `int()`, etc. in `MethodFinder.Result.Instance` have been removed in `KavaRef`.
|
||||
You can directly use `get<String>()`, `get<Int>()`, `invoke<String>(...)`, `invoke<Int>(...)`, etc. to get or call the corresponding type results.
|
||||
|
||||
::: danger
|
||||
|
||||
If you are looking for (filtering) `Field`, you need to note that there may be semantic conflicts between `KavaRef` and `YukiReflection` in the acquisition method of `Field`.
|
||||
Please pay special attention when migrating this part.
|
||||
|
||||
For example, get the static field of `content` in `MyClass`, in `YukiReflection`, you would do this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass::class.java
|
||||
.field { name = "content" } // Return FieldFinder.Result
|
||||
.get() // Cannot be omitted, return FieldFinder.Result.Instance
|
||||
.string() // value
|
||||
```
|
||||
|
||||
In `KavaRef` you need to do this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // Return FieldResolver<MyClass>
|
||||
.get<String>() // value
|
||||
```
|
||||
|
||||
As mentioned above, `get(...)` is used to get the `FieldFinder.Result.Instance` object in Y`ukiReflection`, not the value.
|
||||
To get the value and process it as a specified type, you need to call `string()` or `cast<String>()`, and in `KavaRef`,
|
||||
you use `get<T>()` directly in `MemberResolver` to get the value of the specified type.
|
||||
The usage of `get(...)` of `KavaRef` for `get(...)` to `of(...)`.
|
||||
|
||||
So the complete writing of the above example in `KavaRef` should be.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Since the call is a static instance, "of(null)" can be omitted.
|
||||
MyClass::class.resolve()
|
||||
.firstField { name = "content" } // It's already a call chain object FieldResolver<MyClass>
|
||||
.of(null) // Can be omitted and return to the call chain object FieldResolver<MyClass>
|
||||
.get<String>() // value
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
`KavaRef` no longer provides `call` methods for `Method`, and is now merged uniformly into `invoke` (with generic parameters).
|
||||
At the same time, `KavaRef` defines the `newInstance` method of `Constructor` as `create` (with generic parameters).
|
||||
|
||||
You may have noticed that the condition `superClass()` is gone, it is still there,
|
||||
in `KavaRef` it has been renamed to `superclass()`, docking with the standard Java reflection API.
|
||||
|
||||
At the same time, `KavaRef` extends `KClass`, and you no longer need to use `Some::class.java` to declare an instance of `Class` in most scenarios.
|
||||
|
||||
Another design idea of `KavaRef` is type safety.
|
||||
As long as you use `KClass<T>` and `Class<T>` that declare the generic type, it will be checked and converted to the
|
||||
corresponding type when `of(instance)` and `create(...)`, and type checking will be completed during coding to avoid runtime errors.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Using KavaRef to call and execute.
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.firstMethod {
|
||||
name = "myMethod"
|
||||
parameters(String::class)
|
||||
}
|
||||
// Only instances of type MyClass can be passed in.
|
||||
.of(myClass)
|
||||
.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
## Other Functions
|
||||
|
||||
`KavaRef` and `YukiReflection` are not much different in other functions and extended functions.
|
||||
`KavaRef` separates these functions into a separate module.
|
||||
|
||||
The following functionality is provided in `YukiReflection` but is not implemented and no longer provided in `KavaRef`:
|
||||
|
||||
- Preset reflection type constant classes, such as `StringClass`, `IntType`, etc
|
||||
- You can use Kotlin class references such as `String::class`, `Int::class`, etc. to replace it.
|
||||
For primitive types and wrapper classes, `IntType` is equivalent to `Int::class`, and `IntClass` is equivalent to `JInteger::class`
|
||||
|
||||
- `DexClassFinder` function
|
||||
- Due to its design flaws and possible performance issues when used on Android platforms, it is no longer available for now
|
||||
|
||||
- `RemedyPlan` and `method { ... } .remedys { ... }` functions
|
||||
- Due to possible black box problems, maintenance is relatively difficult.
|
||||
If you need to use similar functions, please implement them manually and will no longer be provided
|
||||
|
||||
- `ClassLoader.listOfClasses()` function
|
||||
- Due to the complex and unstable implementation solutions of each platform, it will no longer be provided
|
||||
|
||||
- `ClassLoader.searchClass()` function
|
||||
- Due to performance issues and design time is limited to Android platform,
|
||||
it is relatively difficult to maintain filter conditions and is no longer provided
|
||||
|
||||
- `Class.hasExtends`, `Class.extends`, `Class.implements` functions
|
||||
- You can replace them with `A::class isSubclassOf B::class`
|
||||
|
||||
- `Class.toJavaPrimitiveType()` function
|
||||
- There is conceptual confusion in functional design and will no longer be provided
|
||||
|
||||
- `"com.some.clazz".hasClass(loader)` function
|
||||
- You can use `loader.hasClass("com.some.clazz")` to replace it
|
||||
|
||||
- `Class.hasField`, `Class.hasMethod`, `Class.hasConstructor` functions
|
||||
- Due to design defects, no longer provided
|
||||
|
||||
- `Class.hasModifiers(...)`, `Member.hasModifiers(...)` functions
|
||||
- You can replace them directly with extension methods such as `Class.isPublic`, `Member.isPublic`
|
||||
|
||||
- `Class.generic()`, `GenericClass` functions
|
||||
- If you just want to get generic parameters of the superclass, you can use `Class.genericSuperclassTypeArguments()`.
|
||||
Due to design defects, no longer provided
|
||||
|
||||
- `Class.current()`, `CurrentClass` functions
|
||||
- Merged into the core function of `KavaRef.resolve()` and is no longer provided separately
|
||||
|
||||
- `Class.buildOf(...)` function
|
||||
- You can use `Class.createInstance(...)` to replace it
|
||||
|
||||
- `Class.allMethods()`, `Class.allFields()`, `Class.allConstructors()` functions
|
||||
- Due to its pollution scope, no longer provided
|
||||
|
||||
- `YLog` log function
|
||||
- `KavaRef` no longer takes over the logs, you can use the corresponding platform implementation method and no longer provided
|
||||
|
||||
## Exception Handling
|
||||
|
||||
`KavaRef` is completely different from `YukiReflection` in exception handling.
|
||||
The exception logic of `KavaRef` will remain transparent by default. <u>**It no longer actively intercepts exceptions and prints error logs or even provides `onNoSuchMethod` listener**</u>.
|
||||
When no valid members are filtered, `KavaRef` will throw an exception directly unless you **explicitly declare the condition as optional (consistent with `YukiReflection` logic)**.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your MyClass instance.
|
||||
val myClass: MyClass
|
||||
// Using KavaRef to call and execute.
|
||||
MyClass::class
|
||||
.resolve()
|
||||
.optional() // Declare as optional, do not throw exceptions.
|
||||
// Use firstMethodOrNull instead of firstMethod,
|
||||
// because the NoSuchElementException of Kotlin itself will be thrown.
|
||||
.firstMethodOrNull {
|
||||
name = "doNonExistentMethod" // Assume that this method does not exist.
|
||||
parameters(String::class)
|
||||
}?.of(myClass)?.invoke("Hello, KavaRef!")
|
||||
```
|
||||
|
||||
For more information, please refer to the [Exception Handling](../library/kavaref-core.md#exception-handling) section in [kavaref-core](../library/kavaref-core.md).
|
||||
|
||||
## New to KavaRef
|
||||
|
||||
If you haven't used `YukiReflection` or `YukiHookAPI`, it doesn't matter, you can refer to the following content to get started quickly.
|
||||
|
||||
::: tip What to Do Next
|
||||
|
||||
For more information, please continue reading [kavaref-core](../library/kavaref-core.md) and [kavaref-extension](../library/kavaref-extension.md).
|
||||
|
||||
Get started using `KavaRef` now!
|
||||
|
||||
:::
|
37
docs-source/src/en/config/processor-resolvers.md
Normal file
37
docs-source/src/en/config/processor-resolvers.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Third-party Member Resolvers
|
||||
|
||||
> Here are some third-party Member resolvers for reference and use.
|
||||
>
|
||||
> Please read [Custom Resolver](../library/kavaref-core.md#custom-resolver) for usage.
|
||||
|
||||
## AndroidHiddenApiBypass
|
||||
|
||||
[Project URL](https://github.com/LSPosed/AndroidHiddenApiBypass)
|
||||
|
||||
> LSPass: Bypass restrictions on non-SDK interfaces
|
||||
|
||||
```kotlin
|
||||
class AndroidHiddenApiBypassResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Constructor<T>>()
|
||||
.toList()
|
||||
return constructors
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Method>()
|
||||
.toList()
|
||||
return methods
|
||||
}
|
||||
}
|
6
docs-source/src/en/config/r8-proguard.md
Normal file
6
docs-source/src/en/config/r8-proguard.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# R8 & Proguard Obfuscate
|
||||
|
||||
> In most scenarios, Android application installation packages can compress volumes through obfuscation.
|
||||
> Here is a configuration method for obfuscation rules.
|
||||
|
||||
`KavaRef` does not require any additional configuration of obfuscation rules in Android projects.
|
46
docs-source/src/en/guide/home.md
Normal file
46
docs-source/src/en/guide/home.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Introduce
|
||||
|
||||
> `KavaRef` is a modern Java reflection API implemented using Kotlin.
|
||||
|
||||
## Background
|
||||
|
||||
This is a modern Java reflection API implemented using Kotlin, designed to provide a cleaner and easier-to-use API while retaining the power of Java
|
||||
reflection.
|
||||
|
||||
The project icon is designed by [MaiTungTM](https://github.com/Lagrio) and is named from **K**otlinJ**avaRef**lection, meaning Java reflection
|
||||
implemented using Kotlin.
|
||||
|
||||
It was firstborn in the [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI), and was later decoupled into
|
||||
the [YukiReflection](https://github.com/HighCapable/YukiReflection) project.
|
||||
|
||||
As you can see, now `KavaRef` is a completely new set of APIs completely refactored with the design idea of `YukiReflection`,
|
||||
which has no affiliation and will replace `YukiReflection` as a new reflection solution.
|
||||
|
||||
If you are using `YukiReflection` or the `YukiHookAPI` project related to it, you can refer to [here](../config/migration) to migrate the reflection API to `KavaRef`.
|
||||
|
||||
## Usage
|
||||
|
||||
`KavaRef` is built in Kotlin **lambda** syntax with Java Builder style.
|
||||
|
||||
It can replace [Java's native Reflection API](https://www.oracle.com/technical-resources/articles/java/javareflection.html) and implement a more complete reflection solution in a more human-friendly language.
|
||||
|
||||
## Skill Requirements
|
||||
|
||||
You must be proficient in Java's native reflection APIs, understand Java's class loading mechanisms, bytecode structures, and how they are used in Kotlin (if you are using Kotlin).
|
||||
|
||||
## Language Requirement
|
||||
|
||||
It is recommended to use Kotlin. API code composition also supports Java,
|
||||
but in pure Java projects `KavaRef` may not be able to play its full functionality and syntactic sugar advantages.
|
||||
|
||||
All the demo sample code in the documentation will be described first using Kotlin.
|
||||
If you don't know how to use Kotlin at all, you may not be able to experience and use the functionality of `KavaRef` more fully.
|
||||
|
||||
## Contribution
|
||||
|
||||
The maintenance of this project is inseparable from the support and contributions of all developers.
|
||||
|
||||
This project is currently in its early stages, and there may still be some problems or lack of functions you need.
|
||||
|
||||
If possible, feel free to submit a PR to contribute features you think are needed to this project or goto [GitHub Issues](repo://issues)
|
||||
to make suggestions to us.
|
97
docs-source/src/en/guide/quick-start.md
Normal file
97
docs-source/src/en/guide/quick-start.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Quick Start
|
||||
|
||||
> Integrate `KavaRef` into your project.
|
||||
|
||||
## Project Requirements
|
||||
|
||||
The project needs to be created using `IntelliJ IDEA` or `Android Studio` and be of type Java or Android
|
||||
project and have integrated Kotlin environment dependencies.
|
||||
|
||||
- IntelliJ IDEA (It is recommended to get the latest version [from here](https://www.jetbrains.com/idea))
|
||||
|
||||
- Android Studio (It is recommended to get the latest version [from here](https://developer.android.com/studio))
|
||||
|
||||
- Kotlin 1.9.0+, Gradle 8+, Java 17+
|
||||
|
||||
### Configure Repositories
|
||||
|
||||
The dependencies of `KavaRef` are published in **Maven Central** and our public repository,
|
||||
you can use the following method to configure repositories.
|
||||
|
||||
We recommend using Kotlin DSL as the Gradle build script language and [SweetDependency](https://github.com/HighCapable/SweetDependency)
|
||||
to manage dependencies.
|
||||
|
||||
#### SweetDependency (Recommended)
|
||||
|
||||
Configure repositories in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
google:
|
||||
maven-central:
|
||||
# (Optional) You can add this URL to use our public repository
|
||||
# When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
|
||||
# For details, please visit: https://github.com/HighCapable/maven-repository
|
||||
highcapable-maven-releases:
|
||||
url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases
|
||||
```
|
||||
|
||||
#### Traditional Method
|
||||
|
||||
Configure repositories in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// (Optional) You can add this URL to use our public repository
|
||||
// When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
|
||||
// For details, please visit: https://github.com/HighCapable/maven-repository
|
||||
maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases")
|
||||
}
|
||||
```
|
||||
|
||||
### Configure Java Version
|
||||
|
||||
Modify the Java version of Kotlin in your project `build.gradle.kts` to 17 or above.
|
||||
|
||||
> Java Project
|
||||
|
||||
```kt
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
```
|
||||
|
||||
> Android Project
|
||||
|
||||
```kt
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Functional Overview
|
||||
|
||||
The project is divided into multiple modules. You can choose the module you wish to include as a dependency in your project, but be sure to include the **kavaref-core** module.
|
||||
|
||||
Click the corresponding module below to view detailed feature descriptions.
|
||||
|
||||
- [kavaref-core](../library/kavaref-core.md)
|
||||
- [kavaref-extension](../library/kavaref-extension.md)
|
||||
|
||||
## Demo
|
||||
|
||||
You can find some samples [here](repo://tree/main/samples) view the corresponding demo project to better understand how these functions work and quickly
|
||||
select the functions you need.
|
38
docs-source/src/en/index.md
Normal file
38
docs-source/src/en/index.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
home: true
|
||||
title: Home
|
||||
heroImage: /images/logo.svg
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /en/guide/home
|
||||
type: primary
|
||||
- text: Changelog
|
||||
link: /en/about/changelog
|
||||
type: secondary
|
||||
features:
|
||||
- title: Light and Elegant
|
||||
details: A powerful, elegant, beautiful API built with Kotlin lambda can help you quickly implement bytecode filtering and reflection functions.
|
||||
- title: Fully Compatible
|
||||
details: Using native Java APIs to implement reflection functionality, it can be used on any Kotlin on JVM project, and it is no problem on Android.
|
||||
- title: Quickly Started
|
||||
details: Simple and easy to use it now! Do not need complex configuration and full development experience, Integrate dependencies and enjoy yourself.
|
||||
footer: Apache-2.0 License | Copyright (C) 2019 HighCapable
|
||||
---
|
||||
|
||||
### Start reflecting anytime, anywhere.
|
||||
|
||||
```java
|
||||
public class World {
|
||||
|
||||
private void sayHello(String content) {
|
||||
System.out.println("Hello " + content + "!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
World().resolve().firstMethod {
|
||||
name = "sayHello"
|
||||
parameters(String::class)
|
||||
}.invoke("KavaRef")
|
||||
```
|
790
docs-source/src/en/library/kavaref-core.md
Normal file
790
docs-source/src/en/library/kavaref-core.md
Normal file
@@ -0,0 +1,790 @@
|
||||
# kavaref-core
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
This is the core dependency of KavaRef, and you need to introduce this module to use the basic features of KavaRef.
|
||||
|
||||
## Configure Dependency
|
||||
|
||||
You can add this module to your project using the following method.
|
||||
|
||||
### SweetDependency (Recommended)
|
||||
|
||||
Add dependency in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-core:
|
||||
version: +
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
Add dependency in your project's `gradle/libs.versions.toml`.
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-core = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-core = { module = "com.highcapable.kavaref:kavaref-core", version.ref = "kavaref-core" }
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.core)
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
### Traditional Method
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-core:<version>")
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
## Function Introduction
|
||||
|
||||
You can view the KDoc [click here](kdoc://kavaref-core).
|
||||
|
||||
### Basic Usage
|
||||
|
||||
KavaRef adopts a chained call design, which creates extension methods for available Java reflection APIs (such as `Class`).
|
||||
You only need to call `resolve()` to these contents to enter the world of KavaRef.
|
||||
|
||||
The relationship diagram is as follows.
|
||||
|
||||
``` :no-line-numbers
|
||||
KavaRef
|
||||
└── KClass/Class/Any.resolve()
|
||||
├── method()
|
||||
├── constructor()
|
||||
└── field()
|
||||
```
|
||||
|
||||
Next, we will give multiple examples of Java `Class`, which will be explained based on them in the future.
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class BaseTest {
|
||||
|
||||
public BaseTest() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void doBaseTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java :no-line-numbers
|
||||
package com.demo;
|
||||
|
||||
public class Test extends BaseTest {
|
||||
|
||||
private Test() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private static TAG = "Test";
|
||||
|
||||
private boolean isTaskRunning = false;
|
||||
|
||||
private void doTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void release(String taskName, Function<boolean, String> task, boolean isFinish) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class Box<T> {
|
||||
|
||||
public void print(T item, String str) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Suppose, we want to get the `doTask` method of `Test` and execute it. In KavaRef, you can do it in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
//Reflect it by instantiating Class.
|
||||
// In KavaRef you don't need to convert it to `java.lang.Class`,
|
||||
// It will automatically call KClass.java.
|
||||
Test::class
|
||||
// Create KavaRef reflections.
|
||||
.resolve()
|
||||
// Create method condition.
|
||||
.method {
|
||||
// Set method name.
|
||||
name = "doTask"
|
||||
// Set method parameter type.
|
||||
parameters(String::class)
|
||||
}
|
||||
// After the condition is executed, the matching List<MethodResolver>
|
||||
// instance will be returned.
|
||||
// Here we get the first filtering result.
|
||||
.first()
|
||||
// Setting an instance of Test on MethodResolver.
|
||||
.of(test)
|
||||
// Calling methods and passing in parameters.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
In the above writing method, we use `Test::class.resolve()` to get the KavaRef reflection instance of the current `Class`.
|
||||
Then create a method filtering condition `MethodCondition` by `method { ... }`, which sets the method name and parameter type, and returns the `List<MethodResolver>` instance after execution.
|
||||
Then we use `first()` to get the first matched `MethodResolver` instance,
|
||||
Then use `of(test)` to set the current instance of `Class`, and finally use `invoke("task_name")` to execute the method and pass in the parameters.
|
||||
|
||||
In this, MethodCondition is inherited from MemberCondition, which allows you to conditionally filter the Method, which contains a conditional image of the Java-core reflection API.
|
||||
You can view the corresponding comments to understand the native usage of each API.
|
||||
|
||||
Similarly, MethodResolver is inherited from `MemberResolver`, which allows you to make reflection calls to `Method` in the filtered result.
|
||||
|
||||
Since the reflection requirement here is to obtain a available method result, the call chain of `method { ... }.first()` may be more cumbersome,
|
||||
and at this time there is the following simplification solution.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class
|
||||
.resolve()
|
||||
// Use firstMethod directly to get the first matching
|
||||
// MethodResolver instance.
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Since we can now get an instance of `Test`, there is also a simplified way to write it,
|
||||
you can use this instance to create KavaRef reflections directly.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Here, the test instance test will be passed to
|
||||
// KavaRef and get test::class.java.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
} // Since you set up an instance, of(test) is no longer needed here.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Next, we need to get the `isTaskRunning` variable, which can be written in the following form.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
val isTaskRunning = test.resolve()
|
||||
.firstField {
|
||||
name = "isTaskRunning"
|
||||
type = Boolean::class
|
||||
}.get<Boolean>()
|
||||
```
|
||||
|
||||
The constructor in `Test` is privatized, and now we can create an instance of it using the following method.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
// For the zero parameter construction method,
|
||||
// the following conditions can be used to filter.
|
||||
// It is equivalent to parameterCount = 0.
|
||||
emptyParameters()
|
||||
}.create() // Create a new Test instance.
|
||||
```
|
||||
|
||||
You can also use `createAsType<T>()` to specify its superclass type `BaseTest` for the actual object `Test`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val test = Test::class.resolve()
|
||||
.firstConstructor {
|
||||
emptyParameters()
|
||||
}.createAsType<BaseTest>() // Create a new BaseTest instance.
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
In addition to methods such as `firstMethod`, you can also use methods such as `lastMethod` to get the last matching `MethodResolver` instance, which is equivalent to `method { ... }.last()`.
|
||||
|
||||
After you get the `MemberResolver` instance, you can use `self` to get the `Member` original instance of the current `MemberResolver` to do some of your own operations.
|
||||
|
||||
In `MemberResolver` inherited from `InstanceAwareResolver` (for example `MethodResolver` and `FieldResolver`), you can use `of(instance)`
|
||||
To set the current instance, if the reflection is static (static) member, you do not need to set the instance.
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
In `MemberResolver` inherited from `InstanceAwareResolver`, the type of `of(instance)` requires the same type as the currently reflected `Class` instance generic type.
|
||||
Unless the `Class` generic type is not specified, or the `Class` generic type is set to `Any`.
|
||||
|
||||
If `of(instance)` appears `Required: Nothing?` error (this is usually due to `Class` created via `Class.forName(...)` or `ClassLoader.loadClass(...)`),
|
||||
then your `Class` is `Class<*>` (in Java it is `Class<?>`).
|
||||
At this time, if you do not want to specify the type, please set or convert it to `Class<Any>`, just like the following.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = Class.forName("com.xxx.MyClass") as Class<Any>
|
||||
// Suppose this is an example of this Class.
|
||||
val myClassInstance: Any
|
||||
myClass.resolve()
|
||||
.firstMethod {
|
||||
// ...
|
||||
}.of(myClassInstance).invoke(...)
|
||||
```
|
||||
|
||||
You can also use [Create Class Object](kavaref-extension.md#create-class-object) provided in [kavaref-extension](kavaref-extension.md) to solve this problem.
|
||||
|
||||
:::
|
||||
|
||||
### Vague Conditions
|
||||
|
||||
You will notice that there is a `release` method in `Test`, but its method parameters are very long and some types may not be directly obtained.
|
||||
|
||||
At this point, you can use the `parameters(...)` condition to use `VagueType` to fill in the method parameter types you don't want to fill in.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "release"
|
||||
// Use VagueType to fill in the types you don't want to fill in,
|
||||
// and ensure that other types can match.
|
||||
parameters(String::class, VagueType, Boolean::class)
|
||||
} // Get this method.
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
`VagueType` can only be used when there are filter conditions with multiple parameters,
|
||||
it cannot be used in filter conditions with only a single parameter, such as `type`.
|
||||
|
||||
You can create it using `VagueType`, `VagueType::class` or `VagueType::class.java`, all of which are correctly recognized as fuzzy filtering conditions.
|
||||
|
||||
:::
|
||||
|
||||
### Freedom Conditions
|
||||
|
||||
In `MemberCondition`, `name`, `type`, `parameterCount` and other conditions can all use the Kotlin lambda feature to create free filtering conditions.
|
||||
|
||||
Suppose we want to get the `doTask` method in `Test`, we can use the following implementation.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
// Use lambda to set the method name.
|
||||
name {
|
||||
// Set the name not case sensitive.
|
||||
it.equals("dotask", ignoreCase = true)
|
||||
}
|
||||
// Set parameter type.
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### Generic Conditions
|
||||
|
||||
KavaRef supports adding generic filtering conditions, which you can use the relevant functions provided by `TypeMatcher`.
|
||||
|
||||
Suppose we need to filter the `print` method in `Box<String>`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val box: Box<String>
|
||||
// Call and execute with KavaRef.
|
||||
box.resolve()
|
||||
.firstMethod {
|
||||
name = "print"
|
||||
// Set generic parameter conditions.
|
||||
genericParametes(
|
||||
// Filter generic name "T".
|
||||
typeVar("T"),
|
||||
// Create TypeMatcher through Class.
|
||||
String::class.toTypeMatcher()
|
||||
)
|
||||
}.invoke("item", "str")
|
||||
```
|
||||
|
||||
### Filter in Superclass
|
||||
|
||||
You will notice that `Test` inherits from `BaseTest`, and now we want to get the `doBaseTask` method of `BaseTest`.
|
||||
|
||||
Without knowing the superclass name, we only need to add `superclass()` to the filter condition to achieve this function.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doBaseTask"
|
||||
parameters(String::class)
|
||||
// Just add this condition.
|
||||
superclass()
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
At this time, we can get this method in the superclass.
|
||||
|
||||
::: tip
|
||||
|
||||
`superclass()` once set it,it will automatically loop backwards whether there is this method in all inherited
|
||||
superclasses until the target has no superclass (the inheritance relationship is `java.lang.Object`).
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
The current filtering method can only filter to the current `Class` method unless the `superclass()` condition is specified,
|
||||
which is the default behavior of the Java reflection API.
|
||||
KavaRef will call `Class.getDeclaredMethods()` to get the current `Class` method instead of `Class.getMethods()`.
|
||||
|
||||
:::
|
||||
|
||||
### Other Conditions
|
||||
|
||||
KavaRef provides some filtering conditions to assist in the use of the Java reflection API.
|
||||
|
||||
Suppose we want to get the contents of the static variable `TAG` in `Test`.
|
||||
|
||||
In order to reflect that the filtering conditions include static descriptors, we can implement them using the following methods.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val tag = Test::class.resolve()
|
||||
.firstField {
|
||||
name = "TAG"
|
||||
type = String::class
|
||||
// Create descriptor filtering.
|
||||
modifiers(Modifiers.STATIC)
|
||||
// Or.
|
||||
modifiers {
|
||||
it.contains(Modifiers.STATIC)
|
||||
}
|
||||
}.get<String>() // Get field content.
|
||||
```
|
||||
|
||||
You can also use string types to pass in full class names in conditions such as `type`, `parameters`, etc.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
// Pass the full class name using string type.
|
||||
parameters("java.lang.String")
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
### Exception Handling
|
||||
|
||||
By default, KavaRef throws an exception when a member is not found during a reflection call.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
} // NoSuchMethodException will be thrown here.
|
||||
```
|
||||
|
||||
If you do not want an exception to be thrown, you can set the optional condition `optional()`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
Test::class.resolve()
|
||||
// Set optional conditions.
|
||||
.optional()
|
||||
.method {
|
||||
name = "doNonExistentMethod"
|
||||
|
||||
} // Return empty List<MethodResolver>.
|
||||
```
|
||||
|
||||
KavaRef prints the complete exception content for debugging, and when using `optional()`, the exception is printed as a WARN level log.
|
||||
|
||||
> The following example
|
||||
|
||||
``` :no-line-numbers
|
||||
No method found matching the condition for current class.
|
||||
+------------------------------------------------+
|
||||
| class com.demo |
|
||||
+------------+-----------------------------------+
|
||||
| name | doNonExistentMethod |
|
||||
| parameters | [class java.lang.String, boolean] |
|
||||
+------------+-----------------------------------+
|
||||
```
|
||||
|
||||
If you don't want KavaRef to throw or print anything, you can use `optional(silent = true)` to silently handle it,
|
||||
but we **do not recommend this**, which will mask the problem unless it is necessary.
|
||||
|
||||
::: danger
|
||||
|
||||
If you set `optional()`, please do not use `firstMethod`, `firstConstructor` and other methods to get a single result.
|
||||
Because they throw an exception with empty list when there is no result, you can use the method with the suffix `OrNull` to get a single result.
|
||||
|
||||
:::
|
||||
|
||||
### Log Management
|
||||
|
||||
KavaRef provides its own log management function, you can set the log level through `KavaRef.logLevel`.
|
||||
|
||||
You can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.DEBUG` to enable DEBUG level logs so that KavaRef
|
||||
prints more detailed step-by-step filtering condition logs to the console during the filtering process.
|
||||
|
||||
If you want to turn off all log printing of KavaRef, you can set `KavaRef.logLevel = KavaRefRuntime.LogLevel.OFF`.
|
||||
|
||||
If you have more advanced requirements, you can implement `KavaRefRuntime.Logger` to customize your own log printing method.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class MyLogger : KavaRefRuntime.Logger {
|
||||
|
||||
// Here you can specify the tag for log printing.
|
||||
override val tag = "MyLogger"
|
||||
|
||||
override fun debug(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun info(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun warn(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
|
||||
override fun error(msg: Any?, throwable: Throwable?) {
|
||||
// Implement your log printing logic here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then, set it to KavaRef.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
KavaRef.setLogger(MyLogger())
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
The above content explains all the usage methods in standard scenarios.
|
||||
If you have a more fine-grained usage scenario, you can manually create related components of KavaRef.
|
||||
|
||||
If you don't like the Kotlin lambda writing, you can create chained calls manually.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Call and execute with KavaRef.
|
||||
test.resolve()
|
||||
.method() // Conditions begin.
|
||||
.name("doTask")
|
||||
.parameters(String::class)
|
||||
.build() // Conditions ends (executes)
|
||||
.first()
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
You can also manually create any filtering conditions to achieve multiplexing it in any reflection.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// Apply condition to reflection object.
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition)
|
||||
.of(test) // Setting up instance.
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
Alternatively, you can also manually and completely implement the entire reflection process.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an example of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
val condition = MethodCondition<Test>()
|
||||
condition.name = "doTask"
|
||||
condition.parameters(String::class)
|
||||
// Create MemberCondition.Configuration manually.
|
||||
val configuration = Test::class.java.createConfiguration(
|
||||
memberInstance = test, // Setting up instance.
|
||||
processorResolver = null, // Use the default resolver, refer to the "Custom Resolver" below.
|
||||
superclass = false, // Whether to filter in superclass.
|
||||
optional = MemberCondition.Configuration.Optional.NO // Configure optional conditions.
|
||||
)
|
||||
// Create and start filtering.
|
||||
val resolvers = condition.build(configuration)
|
||||
// Get the first result.
|
||||
val resolver = resolvers.first()
|
||||
// Execute the method.
|
||||
resolver.invoke("task_name")
|
||||
```
|
||||
|
||||
If you have more advanced requirements for business layer logic, you can also use `mergeWith` to merge multiple filter conditions.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Create MethodCondition manually.
|
||||
// Create the first condition.
|
||||
val condition1 = MethodCondition<Test>()
|
||||
condition1.name = "doTask"
|
||||
// Create a second condition.
|
||||
val condition2 = MethodCondition<Test>()
|
||||
condition2.parameters(String::class)
|
||||
// Merge condition2 into condition1.
|
||||
// At this time, the condition of condition1 will contain the condition that condition2 is not null.
|
||||
// The duplicated conditions in condition1 will be overwritten by the condition2 condition.
|
||||
condition1.mergeWith(condition2)
|
||||
// You can also use the infix syntax.
|
||||
condition1 mergeWith condition2
|
||||
// Call and execute with KavaRef.
|
||||
Test::class.resolve()
|
||||
.firstMethod(condition1)
|
||||
.of(test)
|
||||
.invoke("task_name")
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
Reused use of `build(...)` will no longer be allowed for creation when `MemberCondition.Configuration` is set.
|
||||
At this point you need to use `copy()` to copy and create a new `MemberCondition`.
|
||||
|
||||
Similarly, `InstanceAwareResolver` is not allowed to duplicately set up new instances after setting the instance via `MemberCondition.Configuration.memberInstance` or `of(instance)`.
|
||||
At this time, you also need to use `copy()` to copy and create a new `InstanceAwareResolver`.
|
||||
|
||||
:::
|
||||
|
||||
### Custom Resolver
|
||||
|
||||
KavaRef uses the default `Member` resolver for filtering.
|
||||
If you want to implement your own resolver, you can customize the global and the resolver used for each reflection process.
|
||||
|
||||
You can inherit from `MemberProccessor.Resolver` to implement your own resolver.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class MyMemberProcessorResolver : MemberProcessor.Resolver() {
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
// Intercept and implement your constructor filtering logic here.
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
// Intercept and implement your method filtering logic here.
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredFields(declaringClass: Class<T>): List<Field> {
|
||||
// Intercept and implement your field filtering logic here.
|
||||
return super.getDeclaredFields(declaringClass)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can then set it to the global configuration.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
MemberProcessor.globalResolver = MyMemberProcessorResolver()
|
||||
```
|
||||
|
||||
Alternatively, during each reflection, you can set up a custom resolver using `MemberCondition.Configuration` or use a chain call to set up a resolver.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Create resolver.
|
||||
val myResolver = MyMemberProcessorResolver()
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
test.resolve()
|
||||
// Set custom resolver.
|
||||
.processor(myResolver)
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}.invoke("task_name")
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
You can find some publicly maintained custom solvers in [here](../config/processor-resolvers.md) and define them in your project to use.
|
||||
|
||||
:::
|
||||
|
||||
### About Cache
|
||||
|
||||
Due to the diversity of filtering conditions, KavaRef does not directly provide caching function,
|
||||
and the implementation method of caching will also vary depending on the implementation method of each developer.
|
||||
|
||||
We recommend manually implementing caches of `MemberResolver` created with filter results for improved performance
|
||||
and refer to [Create Manually](#create-manually) to split filter conditions to optimize code reuse.
|
||||
|
||||
::: danger
|
||||
|
||||
If you use `val myResolver by lazy { ... }` to implement the cache, for example, do so below.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myResolver by lazy {
|
||||
Test::class.resolve()
|
||||
.firstMethod {
|
||||
name = "doTask"
|
||||
parameters(String::class)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You may do this when calling.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
myResolver.of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
Please note that since `MemberResolver` is cached, the same instance is called every time you reference it,
|
||||
and the instance object of `MemberResolver` is not allowed to be set duplicately (see the "Pay Attention" below [Create Manually](#create-manually)).
|
||||
|
||||
So calling this directly will throw an exception, you need to change it to the following form.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is an instance of this Class.
|
||||
val test: Test
|
||||
// Call and execute using KavaRef.
|
||||
myResolver.copy().of(test).invoke("task_name")
|
||||
```
|
||||
|
||||
This allows you to copy a new `MemberResolver` instance every time you call without repeating the reflection process and throwing an exception.
|
||||
|
||||
:::
|
||||
|
||||
### Java Usage
|
||||
|
||||
KavaRef is not recommended to be used directly in Java because its API design is based on Kotlin's features and syntax sugar.
|
||||
|
||||
If you need to use KavaRef in Java, you can do it in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Suppose this is an example of this Class.
|
||||
Test test;
|
||||
// Call and execute with KavaRef.
|
||||
KavaRef.resolveClass(Test.class)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.of(test)
|
||||
.invoke("task_name");
|
||||
// Or create KavaRef reflections using an instance.
|
||||
KavaRef.resolveObject(test)
|
||||
.method()
|
||||
.name("doTask")
|
||||
.parameters(String.class)
|
||||
.build()
|
||||
.get(0)
|
||||
.invoke("task_name");
|
||||
}
|
||||
}
|
||||
```
|
315
docs-source/src/en/library/kavaref-extension.md
Normal file
315
docs-source/src/en/library/kavaref-extension.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# kavaref-extension
|
||||
|
||||

|
||||
<span style="margin-left: 5px"/>
|
||||

|
||||
|
||||
This is an extended dependency for KavaRef-related features.
|
||||
|
||||
## Configure Dependency
|
||||
|
||||
You can add this module to your project using the following method.
|
||||
|
||||
### SweetDependency (Recommended)
|
||||
|
||||
Add dependency in your project's `SweetDependency` configuration file.
|
||||
|
||||
```yaml
|
||||
libraries:
|
||||
com.highcapable.kavaref:
|
||||
kavaref-extension:
|
||||
version: +
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
```
|
||||
|
||||
### Version Catalog
|
||||
|
||||
Add dependency in your project's `gradle/libs.versions.toml`.
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
kavaref-extension = "<version>"
|
||||
|
||||
[libraries]
|
||||
kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" }
|
||||
```
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation(libs.kavaref.extension)
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
### Traditional Method
|
||||
|
||||
Configure dependency in your project `build.gradle.kts`.
|
||||
|
||||
```kotlin
|
||||
implementation("com.highcapable.kavaref:kavaref-extension:<version>")
|
||||
```
|
||||
|
||||
Please change `<version>` to the version displayed at the top of this document.
|
||||
|
||||
## Function Introduction
|
||||
|
||||
You can view the KDoc [click here](kdoc://kavaref-extension).
|
||||
|
||||
### Class Extensions
|
||||
|
||||
KavaRef provides some extensions that are more convenient when dealing with `Class` objects.
|
||||
|
||||
KavaRef also adds the `KClass` extensions to the `Class` extensions,
|
||||
which is used to call `KClass.java`, which is more concise than using `Some::class.java` directly.
|
||||
|
||||
#### Create Class Object
|
||||
|
||||
For example, we need to create a `Class` object using the string class name.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = "com.example.MyClass".toClass()
|
||||
// You can use a method with OrNull suffix to return null
|
||||
// when Class is not found instead of throwing an exception.
|
||||
val myClassOrNull = "com.example.MyClass".toClassOrNull()
|
||||
```
|
||||
|
||||
These methods use `ClassLoaderProvider` to get the default `ClassLoader`,
|
||||
you can set the default `ClassLoader` to affect global functionality.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
ClassLoaderProvider.classLoader = MyCustomClassLoader()
|
||||
```
|
||||
|
||||
You can also manually pass a `ClassLoader` parameter to the `toClass` method to specify which `ClassLoader` to use.
|
||||
|
||||
#### Class Object Reference
|
||||
|
||||
Referring to Java Class in Kotlin requires writing a very long statement,
|
||||
such as `MyClass::class.java`, which you can simplify in the following ways.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = classOf<MyClass>()
|
||||
```
|
||||
|
||||
You can use the `isSubclassOf` method to determine whether a `Class` is another `Class` subclass.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val isSubclass = MyClass::class isSubclassOf MySuperClass::class
|
||||
// Of course, it also has a corresponding antonym of judgment.
|
||||
val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class
|
||||
```
|
||||
|
||||
You can also use the `hasSuperclass` and `hasInterfaces` methods to determine whether a `Class` has a superclass or an interface.
|
||||
|
||||
::: danger
|
||||
|
||||
The `Class` passed in by the `classOf` method will perform unboxing of Java wrapper classes by default,
|
||||
whether you pass in something like `kotlin.Boolean` or `java.lang.Boolean` (see [Java Wrapper Classes Extensions](#java-wrapper-classes-extensions) below),
|
||||
If you need to avoid the incoming `Class` being unboxed into primitive types, you need to explicitly set the `primitiveType = false` parameter.
|
||||
|
||||
:::
|
||||
|
||||
#### Create New Instance
|
||||
|
||||
KavaRef provides a way for `Class` to easily create a new instance.
|
||||
You don't need to consider the type of constructing parameters,
|
||||
you just need to pass in the corresponding parameters to create a new instance immediately.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass = MyClass::class.createInstance("Hello", 123)
|
||||
// You can also use a method with the OrNull suffix to return null
|
||||
// when creation fails instead of throwing an exception.
|
||||
val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123)
|
||||
// The createInstance method only filters public constructors by default.
|
||||
// If you need to call non-public constructors, please set isPublic = false.
|
||||
val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false)
|
||||
// If you want to specify the type to create an instance to use another type,
|
||||
// you can use the following method.
|
||||
val mySuperClass = MyClass::class.createInstanceAsType<MySuperClass>("Hello", 123)
|
||||
// Similarly, you can use a method with the OrNull suffix to return null when
|
||||
// creation fails instead of throwing an exception.
|
||||
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("Hello", 123)
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
After the `createInstance` method is successfully matched once, it will cache the results to prevent performance losses
|
||||
caused by duplicated reflections. It is thread-safe and you can use it in any standard scenario with confidence.
|
||||
|
||||
:::
|
||||
|
||||
::: danger
|
||||
|
||||
When you pass in a parameter with `null`, KavaRef tries to use it as part of the matchable condition (vague condition), and the accuracy may decrease.
|
||||
|
||||
The `createInstance` method does not allow all parameters to be `null` (the conditions are completely vague),
|
||||
and an exception will be thrown directly because this situation cannot be determined which instance to create.
|
||||
|
||||
:::
|
||||
|
||||
#### Class Modifier
|
||||
|
||||
KavaRef also extends `Modifier`, you can directly use `Class.isPublic` and other methods to judge a `Class` modifier.
|
||||
|
||||
#### VariousClass
|
||||
|
||||
KavaRef provides the `VariousClass` class to load the `Class` object with an indeterminate full class name and return the first match successfully.
|
||||
|
||||
This feature is usually used for class names in Android apps that are obfuscated by R8.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that in version A, this class is com.example.a,
|
||||
// In version B, this class is com.example.b.
|
||||
val myClass = VariousClass("com.example.a", "com.example.b").load()
|
||||
// You can also use a method with the suffix OrNull to return null
|
||||
// instead of throwing an exception if Class is not found.
|
||||
val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull()
|
||||
```
|
||||
|
||||
#### Lazy Loading Class Object
|
||||
|
||||
KavaRef provides the `LazyClass` class to lazy loading the `Class` object.
|
||||
|
||||
You can load `Class` when needed, instead of loading it immediately when created,
|
||||
which can solve some `Class` that need to be loaded when run or run to specific conditions.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Define a Class that cannot be loaded for null and hosts it to myClass.
|
||||
val myClass by lazyClass("com.example.MyClass")
|
||||
// Define a Class that can be loaded for null delay and host it to myClassOrNull.
|
||||
val myClassOrNull by lazyClassOrNull("com.example.MyClass")
|
||||
// It can also support incoming VariousClass.
|
||||
val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b"))
|
||||
// Called and loaded when needed.
|
||||
myClass.resolve()
|
||||
myClassOrNull?.resolve()
|
||||
otherClassOrNull?.resolve()
|
||||
```
|
||||
|
||||
#### ClassLoader Extensions
|
||||
|
||||
KavaRef also provides some practical extension methods for `ClassLoader`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Assume that's your ClassLoader.
|
||||
val classLoader: ClassLoader
|
||||
// Load a Class and return null if the load fails.
|
||||
val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass")
|
||||
// Determine whether this Class exists in the current ClassLoader.
|
||||
val isClassExists = classLoader.hasClass("com.example.MyClass")
|
||||
```
|
||||
|
||||
### Array Class Extensions
|
||||
|
||||
In Java, the `Class` object of an array is a special `Class` object, and usually we create it as follows.
|
||||
|
||||
For example, create a `Class` object of `java.lang.String[]`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass
|
||||
```
|
||||
|
||||
This is very long to write and is not convenient to maintain, so KavaRef provides a way to simplify this process.
|
||||
|
||||
Now, the `Class` object that creates `java.lang.String[]` can be written like this.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val arrayClass = ArrayClass(String::class)
|
||||
```
|
||||
|
||||
### Member Extensions
|
||||
|
||||
KavaRef provides some extension methods to simplify operations on `Member`.
|
||||
|
||||
You can set its accessibility using the `makeAccessible` method on any `Member` object.
|
||||
|
||||
It will take effect if `Member` is the `AccessibleObject` type.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
// Suppose this is your current Member object.
|
||||
val method: Method
|
||||
// Make method is accessible.
|
||||
method.makeAccessible()
|
||||
```
|
||||
|
||||
Similarly, KavaRef also extends `Modifier`, and you can directly use `Member.isPublic` and other methods to judge a `Member` modifier.
|
||||
|
||||
### Type Extensions
|
||||
|
||||
When manipulating types or generic types in Java, you usually need to use the `Type` interface and its subinterface to handle it.
|
||||
|
||||
KavaRef provides some extension methods to simplify operations on `Type`.
|
||||
|
||||
For example, you can convert a `Type` that meets the requirements to a `Class` object.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val clazz = type.toClass()
|
||||
// You can also use a method with the suffix named OrNull to
|
||||
// return null when the conversion fails instead of throwing an exception.
|
||||
val clazzOrNull = type.toClassOrNull()
|
||||
```
|
||||
|
||||
You can also convert `Type` that meets the requirements to `ParameterizedType` object.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val type: Type
|
||||
val parameterizedType = type.asParameterizedType()
|
||||
// You can also use a method with the suffix named OrNull to
|
||||
// return null when the conversion fails instead of throwing an exception.
|
||||
val parameterizedTypeOrNull = type.asParameterizedTypeOrNull()
|
||||
```
|
||||
|
||||
You can also use the following method to get the generic parameter array in the superclass,
|
||||
which is often used in some superclass and subclass encapsulation operations.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
val myClass: Class<*>
|
||||
// Get the generic parameter array of myClass superclass.
|
||||
// If the acquisition fails or cannot be retrieved, the empty array will be returned.
|
||||
val arguments = myClass.genericSuperclassTypeArguments()
|
||||
```
|
||||
|
||||
### Java Wrapper Classes Extensions
|
||||
|
||||
In Kotlin, you can directly use `Boolean::class`, `Byte::class`, etc. to obtain Java's original types `boolean` and `byte` instead of their wrapper classes.
|
||||
|
||||
If you need to get Java wrapper classes, you need to use the complete `java.lang.Boolean::class`, `java.lang.Byte::class`, etc.
|
||||
or use `Boolean::class.javaObjectType`, `Byte::class.javaObjectType`.
|
||||
|
||||
So, KavaRef provides some type alias to handle Java wrapper classes. Now you only need to prefix `J` to these types, such as `JBoolean::class`.
|
||||
It is equivalent to `java.lang.Boolean::class`, and some types need to be filled in the full name, such as `JInteger::class`.
|
Reference in New Issue
Block a user