diff --git a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
index e4ae46f4..ec8b038e 100644
--- a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
+++ b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
@@ -70,17 +70,73 @@ inline fun with(initiate: NameSpace.() -> Unit): NameSpace
> 创建一个调用空间。
+### dataMaxByteSize - field
+
+```kotlin:no-line-numbers
+var dataMaxByteSize: Int
+```
+
+**Change Records**
+
+`v1.1.9` `added`
+
+**Function Illustrate**
+
+> `YukiHookDataChannel` 允许发送的最大数据字节大小。
+
+默认为 `500 KB (500 * 1024)`,详情请参考 `receiverDataMaxByteSize` 的注释。
+
+最小不能低于 `100 KB (100 * 1024)`,否则会被重新设置为 `100 KB (100 * 1024)`。
+
+设置后将在全局生效,直到当前进程结束。
+
+超出最大数据字节大小后的数据将被自动分段发送。
+
+::: danger
+
+请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
+
+:::
+
+### dataMaxByteCompressionFactor - field
+
+```kotlin:no-line-numbers
+var dataMaxByteCompressionFactor: Int
+```
+
+**Change Records**
+
+`v1.1.9` `added`
+
+**Function Illustrate**
+
+> `YukiHookDataChannel` 允许发送的最大数据字节大小倍数 (分段数据)。
+
+默认为 `3`,详情请参考 `receiverDataMaxByteCompressionFactor` 的注释。
+
+最小不能低于 `2`,否则会被重新设置为 `2`。
+
+设置后将在全局生效,直到当前进程结束。
+
+超出最大数据字节大小后的数据将按照此倍数自动划分 `receiverDataMaxByteSize` 的大小。
+
+::: danger
+
+请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
+
+:::
+
### allowSendTooLargeData - method
```kotlin:no-line-numbers
fun allowSendTooLargeData(): NameSpace
```
-**变更记录**
+**Change Records**
`v1.1.5` `added`
-**功能描述**
+**Function Illustrate**
> 解除发送数据的大小限制并禁止开启分段发送功能。
diff --git a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
index 0aaa2550..0975fe7d 100644
--- a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
+++ b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.md
@@ -62,6 +62,62 @@ inline fun with(initiate: NameSpace.() -> Unit): NameSpace
> 创建一个调用空间。
+### dataMaxByteSize - field
+
+```kotlin:no-line-numbers
+var dataMaxByteSize: Int
+```
+
+**变更记录**
+
+`v1.1.9` `新增`
+
+**功能描述**
+
+> `YukiHookDataChannel` 允许发送的最大数据字节大小。
+
+默认为 `500 KB (500 * 1024)`,详情请参考 `receiverDataMaxByteSize` 的注释。
+
+最小不能低于 `100 KB (100 * 1024)`,否则会被重新设置为 `100 KB (100 * 1024)`。
+
+设置后将在全局生效,直到当前进程结束。
+
+超出最大数据字节大小后的数据将被自动分段发送。
+
+::: danger
+
+请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
+
+:::
+
+### dataMaxByteCompressionFactor - field
+
+```kotlin:no-line-numbers
+var dataMaxByteCompressionFactor: Int
+```
+
+**变更记录**
+
+`v1.1.9` `新增`
+
+**功能描述**
+
+> `YukiHookDataChannel` 允许发送的最大数据字节大小倍数 (分段数据)。
+
+默认为 `3`,详情请参考 `receiverDataMaxByteCompressionFactor` 的注释。
+
+最小不能低于 `2`,否则会被重新设置为 `2`。
+
+设置后将在全局生效,直到当前进程结束。
+
+超出最大数据字节大小后的数据将按照此倍数自动划分 `receiverDataMaxByteSize` 的大小。
+
+::: danger
+
+请谨慎调整此参数,如果超出了系统能够允许的大小会引发 **TransactionTooLargeException** 异常。
+
+:::
+
### allowSendTooLargeData - method
```kotlin:no-line-numbers
diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt
index fb8458dd..64303134 100644
--- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt
+++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt
@@ -40,12 +40,10 @@ import android.content.IntentFilter
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
+import android.os.TransactionTooLargeException
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.CauseProblemsApi
-import com.highcapable.yukihookapi.hook.log.YukiHookLogger
-import com.highcapable.yukihookapi.hook.log.YukiLoggerData
-import com.highcapable.yukihookapi.hook.log.yLoggerE
-import com.highcapable.yukihookapi.hook.log.yLoggerW
+import com.highcapable.yukihookapi.hook.log.*
import com.highcapable.yukihookapi.hook.utils.RandomSeed
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
@@ -55,7 +53,6 @@ import com.highcapable.yukihookapi.hook.xposed.channel.priority.ChannelPriority
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import java.io.Serializable
import java.util.concurrent.ConcurrentHashMap
-import kotlin.math.round
/**
* 实现 Xposed 模块的数据通讯桥
@@ -80,17 +77,6 @@ class YukiHookDataChannel private constructor() {
/** 自动生成的 Xposed 模块构建版本号 */
private val moduleGeneratedVersion = YukiHookAPI.Status.compiledTimestamp.toString()
- /**
- * 系统广播允许发送的最大数据字节大小
- *
- * 标准为 1 MB - 实测不同系统目前已知能得到的数据分别有 1、2、3 MB
- *
- * 经过测试分段发送 900 KB 数据在 1 台 Android 13 系统的设备上依然会发生异常
- *
- * 综上所述 - 为了防止系统不同限制不同 - 最终决定设置为 500 KB - 超出后以此大小分段发送数据
- */
- private const val RECEIVER_DATA_MAX_BYTE_SIZE = 500 * 1024
-
/** 模块构建版本号获取标签 */
private const val GET_MODULE_GENERATED_VERSION = "module_generated_version_get"
@@ -106,6 +92,34 @@ class YukiHookDataChannel private constructor() {
/** 仅监听结果键值 */
private const val VALUE_WAIT_FOR_LISTENER = "wait_for_listener_value"
+ /**
+ * 系统广播允许发送的最大数据字节大小
+ *
+ * 标准为 1 MB - 实测不同系统目前已知能得到的数据分别有 1、2、3 MB
+ *
+ * 经过测试分段发送 900 KB 数据在 1 台 Android 13 系统的设备上依然会发生异常
+ *
+ * 综上所述 - 为了防止系统不同限制不同 - 最终决定默认设置为 500 KB - 超出后以此大小分段发送数据
+ */
+ private var receiverDataMaxByteSize = 500 * 1024
+
+ /**
+ * 系统广播允许发送的最大数据字节大小倍数 (分段数据)
+ *
+ * 分段后的数据每次也不能超出 [receiverDataMaxByteSize] 的大小
+ *
+ * 此倍数被作用于分配 [receiverDataMaxByteSize] 的大小
+ *
+ * 倍数计算公式为 [receiverDataMaxByteSize] / [receiverDataMaxByteCompressionFactor] = [receiverDataSegmentMaxByteSize]
+ */
+ private var receiverDataMaxByteCompressionFactor = 3
+
+ /**
+ * 获取当前系统广播允许的最大单个分段数据字节大小
+ * @return [Int]
+ */
+ private val receiverDataSegmentMaxByteSize get() = receiverDataMaxByteSize / receiverDataMaxByteCompressionFactor
+
/** 当前 [YukiHookDataChannel] 单例 */
private var instance: YukiHookDataChannel? = null
@@ -127,7 +141,7 @@ class YukiHookDataChannel private constructor() {
/** 当前注册广播的 [Context] */
private var receiverContext: Context? = null
- /** 是否允许发送超出 [RECEIVER_DATA_MAX_BYTE_SIZE] 大小的数据 */
+ /** 是否允许发送超出 [receiverDataMaxByteSize] 大小的数据 */
private var isAllowSendTooLargeData = false
/** 广播接收器 */
@@ -268,6 +282,53 @@ class YukiHookDataChannel private constructor() {
*/
private val keyNonRepeatName get() = "_${packageName.hashCode()}"
+ /**
+ * 创建一个调用空间
+ * @param initiate 方法体
+ * @return [NameSpace] 可继续向下监听
+ */
+ inline fun with(initiate: NameSpace.() -> Unit) = apply(initiate)
+
+ /**
+ * [YukiHookDataChannel] 允许发送的最大数据字节大小
+ *
+ * 默认为 500 KB (500 * 1024) - 详情请参考 [receiverDataMaxByteSize] 的注释
+ *
+ * 最小不能低于 100 KB (100 * 1024) - 否则会被重新设置为 100 KB (100 * 1024)
+ *
+ * 设置后将在全局生效 - 直到当前进程结束
+ *
+ * - 超出最大数据字节大小后的数据将被自动分段发送
+ *
+ * - ❗警告:请谨慎调整此参数 - 如果超出了系统能够允许的大小会引发 [TransactionTooLargeException] 异常
+ * @return [Int]
+ */
+ var dataMaxByteSize
+ get() = receiverDataMaxByteSize
+ set(value) {
+ receiverDataMaxByteSize = if (value < 100 * 1024) 100 * 1024 else value
+ }
+
+ /**
+ * [YukiHookDataChannel] 允许发送的最大数据字节大小倍数 (分段数据)
+ *
+ * 默认为 3 - 详情请参考 [receiverDataMaxByteCompressionFactor] 的注释
+ *
+ * 最小不能低于 2 - 否则会被重新设置为 2
+ *
+ * 设置后将在全局生效 - 直到当前进程结束
+ *
+ * - 超出最大数据字节大小后的数据将按照此倍数自动划分 [receiverDataMaxByteSize] 的大小
+ *
+ * - ❗警告:请谨慎调整此参数 - 如果超出了系统能够允许的大小会引发 [TransactionTooLargeException] 异常
+ * @return [Int]
+ */
+ var dataMaxByteCompressionFactor
+ get() = receiverDataMaxByteCompressionFactor
+ set(value) {
+ receiverDataMaxByteCompressionFactor = if (value < 2) 2 else value
+ }
+
/**
* 解除发送数据的大小限制并禁止开启分段发送功能
*
@@ -284,13 +345,6 @@ class YukiHookDataChannel private constructor() {
return this
}
- /**
- * 创建一个调用空间
- * @param initiate 方法体
- * @return [NameSpace] 可继续向下监听
- */
- inline fun with(initiate: NameSpace.() -> Unit) = apply(initiate)
-
/**
* 发送键值数据
* @param key 键值名称
@@ -409,35 +463,37 @@ class YukiHookDataChannel private constructor() {
ChannelDataWrapper(id, size > 0, size, index, this)
/**
- * 计算 [ChannelData] 所占空间的字节大小
+ * 计算任意类型所占空间的字节大小
* @return [Int] 字节大小
*/
- private fun ChannelData<*>.calDataByteSize(): Int {
+ private fun Any.calDataByteSize(): Int {
+ val key = if (this is ChannelData<*>) key else "placeholder"
+ val value = if (this is ChannelData<*>) value else this
val bundle = Bundle().apply {
when (value) {
null -> Unit
- is Boolean -> putBoolean(key, value as Boolean)
- is BooleanArray -> putBooleanArray(key, value as BooleanArray)
- is Byte -> putByte(key, value as Byte)
- is ByteArray -> putByteArray(key, value as ByteArray)
- is Char -> putChar(key, value as Char)
- is CharArray -> putCharArray(key, value as CharArray)
- is Double -> putDouble(key, value as Double)
- is DoubleArray -> putDoubleArray(key, value as DoubleArray)
- is Float -> putFloat(key, value as Float)
- is FloatArray -> putFloatArray(key, value as FloatArray)
- is Int -> putInt(key, value as Int)
- is IntArray -> putIntArray(key, value as IntArray)
- is Long -> putLong(key, value as Long)
- is LongArray -> putLongArray(key, value as LongArray)
- is Short -> putShort(key, value as Short)
- is ShortArray -> putShortArray(key, value as ShortArray)
- is String -> putString(key, value as String)
- is Array<*> -> putSerializable(key, value as Array<*>)
- is CharSequence -> putCharSequence(key, value as CharSequence)
- is Parcelable -> putParcelable(key, value as Parcelable)
- is Serializable -> putSerializable(key, value as Serializable)
- else -> error("Key-Value type ${value?.javaClass?.name} is not allowed")
+ is Boolean -> putBoolean(key, value)
+ is BooleanArray -> putBooleanArray(key, value)
+ is Byte -> putByte(key, value)
+ is ByteArray -> putByteArray(key, value)
+ is Char -> putChar(key, value)
+ is CharArray -> putCharArray(key, value)
+ is Double -> putDouble(key, value)
+ is DoubleArray -> putDoubleArray(key, value)
+ is Float -> putFloat(key, value)
+ is FloatArray -> putFloatArray(key, value)
+ is Int -> putInt(key, value)
+ is IntArray -> putIntArray(key, value)
+ is Long -> putLong(key, value)
+ is LongArray -> putLongArray(key, value)
+ is Short -> putShort(key, value)
+ is ShortArray -> putShortArray(key, value)
+ is String -> putString(key, value)
+ is Array<*> -> putSerializable(key, value)
+ is CharSequence -> putCharSequence(key, value)
+ is Parcelable -> putParcelable(key, value)
+ is Serializable -> putSerializable(key, value)
+ else -> error("Key-Value type ${value.javaClass.name} is not allowed")
}
}
return runCatching {
@@ -529,7 +585,7 @@ class YukiHookDataChannel private constructor() {
fun loggerForTooLargeData(name: String, size: Int) {
if (YukiHookAPI.Configs.isDebug) yLoggerW(
msg = "This data key of \"${wrapper.instance.key}\" type $name is too large (total ${dataByteSize / 1024f} KB, " +
- "limit ${RECEIVER_DATA_MAX_BYTE_SIZE / 1024f} KB), will be segmented to $size piece to send"
+ "limit ${receiverDataMaxByteSize / 1024f} KB), will be segmented to $size piece to send"
)
}
@@ -540,40 +596,20 @@ class YukiHookDataChannel private constructor() {
fun loggerForUnprocessableData(suggestionMessage: String = "") = yLoggerE(
msg = "YukiHookDataChannel cannot send this data key of \"${wrapper.instance.key}\" type ${wrapper.instance.value?.javaClass}, " +
"because it is too large (total ${dataByteSize / 1024f} KB, " +
- "limit ${RECEIVER_DATA_MAX_BYTE_SIZE / 1024f} KB) and cannot be segmented\n" +
+ "limit ${receiverDataMaxByteSize / 1024f} KB) and cannot be segmented\n" +
(if (suggestionMessage.isNotBlank()) "$suggestionMessage\n" else "") +
"If you want to lift this restriction, use the allowSendTooLargeData function when calling, " +
"but this may cause the app crash"
)
-
- /**
- * 计算需要的大小 (预估大小)
- * @param size 集合大小 (长度)
- * @return [Int]
- */
- fun calIntendCountBySize(size: Int) = round(size / round(dataByteSize.toFloat() / RECEIVER_DATA_MAX_BYTE_SIZE)).toInt()
-
- /**
- * 计算需要的大小 (预估大小)
- * @return [Int]
- */
- fun Collection<*>.calIntendCount() = calIntendCountBySize(size)
-
- /**
- * 计算需要的大小 (预估大小)
- * @return [Int]
- */
- fun Map<*, *>.calIntendCount() = calIntendCountBySize(size)
when {
wrapper.isSegmentsType || isAllowSendTooLargeData -> pushReceiver(wrapper)
- dataByteSize >= RECEIVER_DATA_MAX_BYTE_SIZE -> when (wrapper.instance.value) {
+ dataByteSize >= receiverDataMaxByteSize -> when (wrapper.instance.value) {
is List<*> -> (wrapper.instance.value as List<*>).also { value ->
val segments = arrayListOf>()
var segment = arrayListOf()
- val intendCount = value.calIntendCount()
value.forEach {
segment.add(it)
- if (segment.size >= intendCount) {
+ if (segment.calDataByteSize() >= receiverDataSegmentMaxByteSize) {
segments.add(segment)
segment = arrayListOf()
}
@@ -587,10 +623,9 @@ class YukiHookDataChannel private constructor() {
is Map<*, *> -> (wrapper.instance.value as Map<*, *>).also { value ->
val segments = arrayListOf